+
+ case OP_DIV:
+ case OP_IDIV:
+ // we could set up operands for edx:eax, but there is no real need to
+ // (see is_opr_modified())
+ regmask = (1 << xDX) | (1 << xAX);
+ op->regmask_dst = regmask;
+ op->regmask_src |= regmask;
+ if (op->operand[0].lmod == OPLM_UNSPEC)
+ op->operand[0].lmod = OPLM_DWORD;
+ break;
+
+ case OP_SHL:
+ case OP_SHR:
+ case OP_SAR:
+ case OP_ROL:
+ case OP_ROR:
+ if (op->operand[1].lmod == OPLM_UNSPEC)
+ op->operand[1].lmod = OPLM_BYTE;
+ break;
+
+ case OP_PUSH:
+ if (op->operand[0].lmod == OPLM_UNSPEC
+ && (op->operand[0].type == OPT_CONST
+ || op->operand[0].type == OPT_OFFSET
+ || op->operand[0].type == OPT_LABEL))
+ op->operand[0].lmod = OPLM_DWORD;
+ break;
+
+ // alignment
+ case OP_MOV:
+ if (op->operand[0].type == OPT_REG && op->operand[1].type == OPT_REG
+ && op->operand[0].reg == xDI && op->operand[1].reg == xDI)
+ {
+ op->flags |= OPF_RMD;
+ }
+ break;
+
+ case OP_LEA:
+ if (op->operand[0].type == OPT_REG
+ && op->operand[1].type == OPT_REGMEM)
+ {
+ char buf[16];
+ snprintf(buf, sizeof(buf), "%s+0", op->operand[0].name);
+ if (IS(buf, op->operand[1].name))
+ op->flags |= OPF_RMD;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+static const char *op_name(enum op_op op)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(op_table); i++)
+ if (op_table[i].op == op)
+ return op_table[i].name;
+
+ return "???";
+}
+
+// debug
+static const char *dump_op(struct parsed_op *po)
+{
+ static char out[128];
+ char *p = out;
+ int i;
+
+ if (po == NULL)
+ return "???";
+
+ snprintf(out, sizeof(out), "%s", op_name(po->op));
+ for (i = 0; i < po->operand_cnt; i++) {
+ p += strlen(p);
+ if (i > 0)
+ *p++ = ',';
+ snprintf(p, sizeof(out) - (p - out),
+ po->operand[i].type == OPT_REGMEM ? " [%s]" : " %s",
+ po->operand[i].name);
+ }
+
+ return out;
+}
+
+static const char *lmod_type_u(struct parsed_op *po,
+ enum opr_lenmod lmod)
+{
+ switch (lmod) {
+ case OPLM_DWORD:
+ return "u32";
+ case OPLM_WORD:
+ return "u16";
+ case OPLM_BYTE:
+ return "u8";
+ default:
+ ferr(po, "invalid lmod: %d\n", lmod);
+ return "(_invalid_)";
+ }
+}
+
+static const char *lmod_cast_u(struct parsed_op *po,
+ enum opr_lenmod lmod)
+{
+ switch (lmod) {
+ case OPLM_DWORD:
+ return "";
+ case OPLM_WORD:
+ return "(u16)";
+ case OPLM_BYTE:
+ return "(u8)";
+ default:
+ ferr(po, "invalid lmod: %d\n", lmod);
+ return "(_invalid_)";
+ }
+}
+
+static const char *lmod_cast_u_ptr(struct parsed_op *po,
+ enum opr_lenmod lmod)
+{
+ switch (lmod) {
+ case OPLM_DWORD:
+ return "*(u32 *)";
+ case OPLM_WORD:
+ return "*(u16 *)";
+ case OPLM_BYTE:
+ return "*(u8 *)";
+ default:
+ ferr(po, "invalid lmod: %d\n", lmod);
+ return "(_invalid_)";
+ }
+}
+
+static const char *lmod_cast_s(struct parsed_op *po,
+ enum opr_lenmod lmod)
+{
+ switch (lmod) {
+ case OPLM_DWORD:
+ return "(s32)";
+ case OPLM_WORD:
+ return "(s16)";
+ case OPLM_BYTE:
+ return "(s8)";
+ default:
+ ferr(po, "%s: invalid lmod: %d\n", __func__, lmod);
+ return "(_invalid_)";
+ }
+}
+
+static const char *lmod_cast(struct parsed_op *po,
+ enum opr_lenmod lmod, int is_signed)
+{
+ return is_signed ?
+ lmod_cast_s(po, lmod) :
+ lmod_cast_u(po, lmod);
+}
+
+static int lmod_bytes(struct parsed_op *po, enum opr_lenmod lmod)
+{
+ switch (lmod) {
+ case OPLM_DWORD:
+ return 4;
+ case OPLM_WORD:
+ return 2;
+ case OPLM_BYTE:
+ return 1;
+ default:
+ ferr(po, "%s: invalid lmod: %d\n", __func__, lmod);
+ return 0;
+ }
+}
+
+static const char *opr_name(struct parsed_op *po, int opr_num)
+{
+ if (opr_num >= po->operand_cnt)
+ ferr(po, "opr OOR: %d/%d\n", opr_num, po->operand_cnt);
+ return po->operand[opr_num].name;
+}
+
+static unsigned int opr_const(struct parsed_op *po, int opr_num)
+{
+ if (opr_num >= po->operand_cnt)
+ ferr(po, "opr OOR: %d/%d\n", opr_num, po->operand_cnt);
+ if (po->operand[opr_num].type != OPT_CONST)
+ ferr(po, "opr %d: const expected\n", opr_num);
+ return po->operand[opr_num].val;
+}
+
+static const char *opr_reg_p(struct parsed_op *po, struct parsed_opr *popr)
+{
+ if ((unsigned int)popr->reg >= MAX_REGS)
+ ferr(po, "invalid reg: %d\n", popr->reg);
+ return regs_r32[popr->reg];
+}
+
+// cast1 is the "final" cast
+static const char *simplify_cast(const char *cast1, const char *cast2)
+{
+ static char buf[256];
+
+ if (cast1[0] == 0)
+ return cast2;
+ if (cast2[0] == 0)
+ return cast1;
+ if (IS(cast1, cast2))
+ return cast1;
+ if (IS(cast1, "(s8)") && IS(cast2, "(u8)"))
+ return cast1;
+ if (IS(cast1, "(s16)") && IS(cast2, "(u16)"))
+ return cast1;
+ if (IS(cast1, "(u8)") && IS_START(cast2, "*(u8 *)"))
+ return cast2;
+ if (IS(cast1, "(u16)") && IS_START(cast2, "*(u16 *)"))
+ return cast2;
+ if (strchr(cast1, '*') && IS_START(cast2, "(u32)"))
+ return cast1;
+
+ snprintf(buf, sizeof(buf), "%s%s", cast1, cast2);
+ return buf;
+}
+
+static const char *simplify_cast_num(const char *cast, unsigned int val)
+{
+ if (IS(cast, "(u8)") && val < 0x100)
+ return "";
+ if (IS(cast, "(s8)") && val < 0x80)
+ return "";
+ if (IS(cast, "(u16)") && val < 0x10000)
+ return "";
+ if (IS(cast, "(s16)") && val < 0x8000)
+ return "";
+ if (IS(cast, "(s32)") && val < 0x80000000)
+ return "";
+
+ return cast;
+}
+
+static struct parsed_equ *equ_find(struct parsed_op *po, const char *name,
+ int *extra_offs)
+{
+ const char *p;
+ char *endp;
+ int namelen;
+ int i;
+
+ *extra_offs = 0;
+ namelen = strlen(name);
+
+ p = strchr(name, '+');
+ if (p != NULL) {
+ namelen = p - name;
+ if (namelen <= 0)
+ ferr(po, "equ parse failed for '%s'\n", name);
+
+ if (IS_START(p, "0x"))
+ p += 2;
+ *extra_offs = strtol(p, &endp, 16);
+ if (*endp != 0)
+ ferr(po, "equ parse failed for '%s'\n", name);
+ }
+
+ for (i = 0; i < g_eqcnt; i++)
+ if (strncmp(g_eqs[i].name, name, namelen) == 0
+ && g_eqs[i].name[namelen] == 0)
+ break;
+ if (i >= g_eqcnt) {
+ if (po != NULL)
+ ferr(po, "unresolved equ name: '%s'\n", name);
+ return NULL;
+ }
+
+ return &g_eqs[i];
+}
+
+static int is_stack_access(struct parsed_op *po,
+ const struct parsed_opr *popr)
+{
+ return (parse_stack_el(popr->name, NULL)
+ || (g_bp_frame && !(po->flags & OPF_EBP_S)
+ && IS_START(popr->name, "ebp")));
+}
+
+static void parse_stack_access(struct parsed_op *po,
+ const char *name, char *ofs_reg, int *offset_out,
+ int *stack_ra_out, const char **bp_arg_out)
+{
+ const char *bp_arg = "";
+ const char *p = NULL;
+ struct parsed_equ *eq;
+ char *endp = NULL;
+ int stack_ra = 0;
+ int offset = 0;
+
+ ofs_reg[0] = 0;
+
+ if (IS_START(name, "ebp-")
+ || (IS_START(name, "ebp+") && '0' <= name[4] && name[4] <= '9'))
+ {
+ p = name + 4;
+ if (IS_START(p, "0x"))
+ p += 2;
+ offset = strtoul(p, &endp, 16);
+ if (name[3] == '-')
+ offset = -offset;
+ if (*endp != 0)
+ ferr(po, "ebp- parse of '%s' failed\n", name);
+ }
+ else {
+ bp_arg = parse_stack_el(name, ofs_reg);
+ snprintf(g_comment, sizeof(g_comment), "%s", bp_arg);
+ eq = equ_find(po, bp_arg, &offset);
+ if (eq == NULL)
+ ferr(po, "detected but missing eq\n");
+ offset += eq->offset;
+ }
+
+ if (!strncmp(name, "ebp", 3))
+ stack_ra = 4;
+
+ if (ofs_reg[0] == 0 && stack_ra <= offset && offset < stack_ra + 4)
+ ferr(po, "reference to ra? %d %d\n", offset, stack_ra);
+
+ *offset_out = offset;
+ *stack_ra_out = stack_ra;
+ if (bp_arg_out)
+ *bp_arg_out = bp_arg;
+}
+
+static void 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)
+{
+ enum opr_lenmod tmp_lmod = OPLM_UNSPEC;
+ const char *prefix = "";
+ const char *bp_arg = NULL;
+ char ofs_reg[16] = { 0, };
+ int i, arg_i, arg_s;
+ int unaligned = 0;
+ int stack_ra = 0;
+ int offset = 0;
+ int sf_ofs;
+ int lim;
+
+ if (po->flags & OPF_EBP_S)
+ ferr(po, "stack_frame_access while ebp is scratch\n");
+
+ parse_stack_access(po, name, ofs_reg, &offset, &stack_ra, &bp_arg);
+
+ if (offset > stack_ra)
+ {
+ arg_i = (offset - stack_ra - 4) / 4;
+ if (arg_i < 0 || arg_i >= g_func_pp->argc_stack)
+ {
+ if (g_func_pp->is_vararg
+ && arg_i == g_func_pp->argc_stack && is_lea)
+ {
+ // should be va_list
+ if (cast[0] == 0)
+ cast = "(u32)";
+ snprintf(buf, buf_size, "%sap", cast);
+ return;
+ }
+ ferr(po, "offset %d (%s,%d) doesn't map to any arg\n",
+ offset, bp_arg, arg_i);
+ }
+ if (ofs_reg[0] != 0)
+ ferr(po, "offset reg on arg access?\n");
+
+ for (i = arg_s = 0; i < g_func_pp->argc; i++) {
+ if (g_func_pp->arg[i].reg != NULL)
+ continue;
+ if (arg_s == arg_i)
+ break;
+ arg_s++;
+ }
+ if (i == g_func_pp->argc)
+ ferr(po, "arg %d not in prototype?\n", arg_i);
+
+ popr->is_ptr = g_func_pp->arg[i].type.is_ptr;
+
+ switch (popr->lmod)
+ {
+ case OPLM_BYTE:
+ if (is_lea)
+ ferr(po, "lea/byte to arg?\n");
+ if (is_src && (offset & 3) == 0)
+ snprintf(buf, buf_size, "%sa%d",
+ simplify_cast(cast, "(u8)"), i + 1);
+ else
+ snprintf(buf, buf_size, "%sBYTE%d(a%d)",
+ cast, offset & 3, i + 1);
+ break;
+
+ case OPLM_WORD:
+ if (is_lea)
+ ferr(po, "lea/word to arg?\n");
+ if (offset & 1) {
+ unaligned = 1;
+ if (!is_src) {
+ if (offset & 2)
+ ferr(po, "problematic arg store\n");
+ snprintf(buf, buf_size, "%s((char *)&a%d + 1)",
+ simplify_cast(cast, "*(u16 *)"), i + 1);
+ }
+ else
+ ferr(po, "unaligned arg word load\n");
+ }
+ else if (is_src && (offset & 2) == 0)
+ snprintf(buf, buf_size, "%sa%d",
+ simplify_cast(cast, "(u16)"), i + 1);
+ else
+ snprintf(buf, buf_size, "%s%sWORD(a%d)",
+ cast, (offset & 2) ? "HI" : "LO", i + 1);
+ break;
+
+ case OPLM_DWORD:
+ if (cast[0])
+ prefix = cast;
+ else if (is_src)
+ prefix = "(u32)";
+
+ if (offset & 3) {
+ unaligned = 1;
+ if (is_lea)
+ snprintf(buf, buf_size, "(u32)&a%d + %d",
+ i + 1, offset & 3);
+ else if (!is_src)
+ ferr(po, "unaligned arg store\n");
+ else {
+ // mov edx, [ebp+arg_4+2]; movsx ecx, dx
+ snprintf(buf, buf_size, "%s(a%d >> %d)",
+ prefix, i + 1, (offset & 3) * 8);
+ }
+ }
+ else {
+ snprintf(buf, buf_size, "%s%sa%d",
+ prefix, is_lea ? "&" : "", i + 1);
+ }
+ break;
+
+ default:
+ ferr(po, "bp_arg bad lmod: %d\n", popr->lmod);
+ }
+
+ if (unaligned)
+ snprintf(g_comment, sizeof(g_comment), "%s unaligned", bp_arg);
+
+ // 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)))
+ {
+ 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);
+ }
+ if (popr->is_ptr && popr->lmod != OPLM_DWORD)
+ ferr(po, "bp_arg arg%d: non-dword ptr access\n", i + 1);
+ }
+ else
+ {
+ if (g_stack_fsz == 0)
+ ferr(po, "stack var access without stackframe\n");
+ g_stack_frame_used = 1;
+
+ sf_ofs = g_stack_fsz + offset;
+ lim = (ofs_reg[0] != 0) ? -4 : 0;
+ if (offset > 0 || sf_ofs < lim)
+ ferr(po, "bp_stack offset %d/%d\n", offset, g_stack_fsz);
+
+ if (is_lea)
+ prefix = "(u32)&";
+ else
+ prefix = cast;
+
+ switch (popr->lmod)
+ {
+ case OPLM_BYTE:
+ snprintf(buf, buf_size, "%ssf.b[%d%s%s]",
+ prefix, sf_ofs, ofs_reg[0] ? "+" : "", ofs_reg);
+ break;
+
+ case OPLM_WORD:
+ if ((sf_ofs & 1) || ofs_reg[0] != 0) {
+ // known unaligned or possibly unaligned
+ strcat(g_comment, " unaligned");
+ if (prefix[0] == 0)
+ prefix = "*(u16 *)&";
+ snprintf(buf, buf_size, "%ssf.b[%d%s%s]",
+ prefix, sf_ofs, ofs_reg[0] ? "+" : "", ofs_reg);
+ break;
+ }
+ snprintf(buf, buf_size, "%ssf.w[%d]", prefix, sf_ofs / 2);
+ break;
+
+ case OPLM_DWORD:
+ if ((sf_ofs & 3) || ofs_reg[0] != 0) {
+ // known unaligned or possibly unaligned
+ strcat(g_comment, " unaligned");
+ if (prefix[0] == 0)
+ prefix = "*(u32 *)&";
+ snprintf(buf, buf_size, "%ssf.b[%d%s%s]",
+ prefix, sf_ofs, ofs_reg[0] ? "+" : "", ofs_reg);
+ break;
+ }
+ snprintf(buf, buf_size, "%ssf.d[%d]", prefix, sf_ofs / 4);
+ break;
+
+ default:
+ ferr(po, "bp_stack bad lmod: %d\n", popr->lmod);
+ }
+ }
+}
+
+static void check_label_read_ref(struct parsed_op *po, const char *name)
+{
+ const struct parsed_proto *pp;
+
+ pp = proto_parse(g_fhdr, name);
+ if (pp == NULL)
+ ferr(po, "proto_parse failed for ref '%s'\n", name);
+
+ // currently we can take __cdecl and __stdcall
+ if (pp->is_func && pp->argc_reg != 0)
+ ferr(po, "reg-arg func reference?\n");
+}
+
+static char *out_src_opr(char *buf, size_t buf_size,
+ struct parsed_op *po, struct parsed_opr *popr, const char *cast,
+ int is_lea)
+{
+ char tmp1[256], tmp2[256];
+ char expr[256];
+ char *p;
+ int ret;
+
+ if (cast == NULL)
+ cast = "";
+
+ switch (popr->type) {
+ case OPT_REG:
+ if (is_lea)
+ ferr(po, "lea from reg?\n");
+
+ switch (popr->lmod) {
+ case OPLM_DWORD:
+ snprintf(buf, buf_size, "%s%s", cast, opr_reg_p(po, popr));
+ break;
+ case OPLM_WORD:
+ snprintf(buf, buf_size, "%s%s",
+ simplify_cast(cast, "(u16)"), opr_reg_p(po, popr));
+ break;
+ case OPLM_BYTE:
+ if (popr->name[1] == 'h') // XXX..
+ snprintf(buf, buf_size, "%s(%s >> 8)",
+ simplify_cast(cast, "(u8)"), opr_reg_p(po, popr));
+ else
+ snprintf(buf, buf_size, "%s%s",
+ simplify_cast(cast, "(u8)"), opr_reg_p(po, popr));
+ break;
+ default:
+ ferr(po, "invalid src lmod: %d\n", popr->lmod);
+ }
+ break;
+
+ case OPT_REGMEM:
+ if (is_stack_access(po, popr)) {
+ stack_frame_access(po, popr, buf, buf_size,
+ popr->name, cast, 1, is_lea);
+ break;
+ }
+
+ strcpy(expr, popr->name);
+ if (strchr(expr, '[')) {
+ // special case: '[' can only be left for label[reg] form
+ ret = sscanf(expr, "%[^[][%[^]]]", tmp1, tmp2);
+ if (ret != 2)
+ ferr(po, "parse failure for '%s'\n", expr);
+ if (tmp1[0] == '(') {
+ // (off_4FFF50+3)[eax]
+ p = strchr(tmp1 + 1, ')');
+ if (p == NULL || p[1] != 0)
+ ferr(po, "parse failure (2) for '%s'\n", expr);
+ *p = 0;
+ memmove(tmp1, tmp1 + 1, strlen(tmp1));
+ }
+ snprintf(expr, sizeof(expr), "(u32)&%s + %s", tmp1, tmp2);
+ }
+
+ // XXX: do we need more parsing?
+ if (is_lea) {
+ snprintf(buf, buf_size, "%s", expr);
+ break;
+ }
+
+ snprintf(buf, buf_size, "%s(%s)",
+ simplify_cast(cast, lmod_cast_u_ptr(po, popr->lmod)), expr);
+ break;
+
+ case OPT_LABEL:
+ check_label_read_ref(po, popr->name);
+ if (cast[0] == 0 && popr->is_ptr)
+ cast = "(u32)";
+
+ if (is_lea)
+ snprintf(buf, buf_size, "(u32)&%s", popr->name);
+ else if (popr->size_lt)
+ snprintf(buf, buf_size, "%s%s%s%s", cast,
+ lmod_cast_u_ptr(po, popr->lmod),
+ popr->is_array ? "" : "&",
+ popr->name);
+ else
+ snprintf(buf, buf_size, "%s%s%s", cast, popr->name,
+ popr->is_array ? "[0]" : "");
+ break;
+
+ case OPT_OFFSET:
+ check_label_read_ref(po, popr->name);
+ if (cast[0] == 0)
+ cast = "(u32)";
+ if (is_lea)
+ ferr(po, "lea an offset?\n");
+ snprintf(buf, buf_size, "%s&%s", cast, popr->name);
+ break;
+
+ case OPT_CONST:
+ if (is_lea)
+ ferr(po, "lea from const?\n");
+
+ printf_number(tmp1, sizeof(tmp1), popr->val);
+ snprintf(buf, buf_size, "%s%s",
+ simplify_cast_num(cast, popr->val), tmp1);
+ break;
+
+ default:
+ ferr(po, "invalid src type: %d\n", popr->type);
+ }
+
+ return buf;
+}
+
+// note: may set is_ptr (we find that out late for ebp frame..)
+static char *out_dst_opr(char *buf, size_t buf_size,
+ struct parsed_op *po, struct parsed_opr *popr)
+{
+ switch (popr->type) {
+ case OPT_REG:
+ switch (popr->lmod) {
+ case OPLM_DWORD:
+ snprintf(buf, buf_size, "%s", opr_reg_p(po, popr));
+ break;
+ case OPLM_WORD:
+ // ugh..
+ snprintf(buf, buf_size, "LOWORD(%s)", opr_reg_p(po, popr));
+ break;
+ case OPLM_BYTE:
+ // ugh..
+ if (popr->name[1] == 'h') // XXX..
+ snprintf(buf, buf_size, "BYTE1(%s)", opr_reg_p(po, popr));
+ else
+ snprintf(buf, buf_size, "LOBYTE(%s)", opr_reg_p(po, popr));
+ break;
+ default:
+ ferr(po, "invalid dst lmod: %d\n", popr->lmod);
+ }
+ break;
+
+ case OPT_REGMEM:
+ if (is_stack_access(po, popr)) {
+ stack_frame_access(po, popr, buf, buf_size,
+ popr->name, "", 0, 0);
+ break;
+ }
+
+ return out_src_opr(buf, buf_size, po, popr, NULL, 0);
+
+ case OPT_LABEL:
+ if (popr->size_mismatch)
+ snprintf(buf, buf_size, "%s%s%s",
+ lmod_cast_u_ptr(po, popr->lmod),
+ popr->is_array ? "" : "&", popr->name);
+ else
+ snprintf(buf, buf_size, "%s%s", popr->name,
+ popr->is_array ? "[0]" : "");
+ break;
+
+ default:
+ ferr(po, "invalid dst type: %d\n", popr->type);
+ }
+
+ return buf;
+}
+
+static char *out_src_opr_u32(char *buf, size_t buf_size,
+ struct parsed_op *po, struct parsed_opr *popr)
+{
+ return out_src_opr(buf, buf_size, po, popr, NULL, 0);
+}
+
+static enum parsed_flag_op split_cond(struct parsed_op *po,
+ enum op_op op, int *is_inv)
+{
+ *is_inv = 0;
+
+ switch (op) {
+ case OP_JO:
+ return PFO_O;
+ case OP_JC:
+ return PFO_C;
+ case OP_JZ:
+ return PFO_Z;
+ case OP_JBE:
+ return PFO_BE;
+ case OP_JS:
+ return PFO_S;
+ case OP_JP:
+ return PFO_P;
+ case OP_JL:
+ return PFO_L;
+ case OP_JLE:
+ return PFO_LE;
+
+ case OP_JNO:
+ *is_inv = 1;
+ return PFO_O;
+ case OP_JNC:
+ *is_inv = 1;
+ return PFO_C;
+ case OP_JNZ:
+ *is_inv = 1;
+ return PFO_Z;
+ case OP_JA:
+ *is_inv = 1;
+ return PFO_BE;
+ case OP_JNS:
+ *is_inv = 1;
+ return PFO_S;
+ case OP_JNP:
+ *is_inv = 1;
+ return PFO_P;
+ case OP_JGE:
+ *is_inv = 1;
+ return PFO_L;
+ case OP_JG:
+ *is_inv = 1;
+ return PFO_LE;
+
+ case OP_ADC:
+ case OP_SBB:
+ return PFO_C;
+
+ default:
+ ferr(po, "split_cond: bad op %d\n", op);
+ return -1;
+ }
+}
+
+static void out_test_for_cc(char *buf, size_t buf_size,
+ struct parsed_op *po, enum parsed_flag_op pfo, int is_inv,
+ enum opr_lenmod lmod, const char *expr)
+{
+ const char *cast, *scast;
+
+ cast = lmod_cast_u(po, lmod);
+ scast = lmod_cast_s(po, lmod);
+
+ switch (pfo) {
+ case PFO_Z:
+ case PFO_BE: // CF=1||ZF=1; CF=0
+ snprintf(buf, buf_size, "(%s%s %s 0)",
+ cast, expr, is_inv ? "!=" : "==");
+ break;
+
+ case PFO_S:
+ case PFO_L: // SF!=OF; OF=0
+ snprintf(buf, buf_size, "(%s%s %s 0)",
+ scast, expr, is_inv ? ">=" : "<");
+ break;
+
+ case PFO_LE: // ZF=1||SF!=OF; OF=0
+ snprintf(buf, buf_size, "(%s%s %s 0)",
+ scast, expr, is_inv ? ">" : "<=");
+ break;
+
+ default:
+ ferr(po, "%s: unhandled parsed_flag_op: %d\n", __func__, pfo);
+ }
+}
+
+static void out_cmp_for_cc(char *buf, size_t buf_size,
+ struct parsed_op *po, enum parsed_flag_op pfo, int is_inv)
+{
+ const char *cast, *scast, *cast_use;
+ char buf1[256], buf2[256];
+ enum opr_lenmod lmod;
+
+ if (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;
+
+ cast = lmod_cast_u(po, lmod);
+ scast = lmod_cast_s(po, lmod);
+
+ switch (pfo) {
+ case PFO_C:
+ case PFO_Z:
+ case PFO_BE: // !a
+ cast_use = cast;
+ break;
+
+ case PFO_S:
+ case PFO_L: // !ge
+ case PFO_LE:
+ cast_use = scast;
+ break;
+
+ default:
+ ferr(po, "%s: unhandled parsed_flag_op: %d\n", __func__, pfo);
+ }
+
+ 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);
+
+ switch (pfo) {
+ case PFO_C:
+ // note: must be unsigned compare
+ snprintf(buf, buf_size, "(%s %s %s)",
+ buf1, is_inv ? ">=" : "<", buf2);
+ break;
+
+ case PFO_Z:
+ snprintf(buf, buf_size, "(%s %s %s)",
+ buf1, is_inv ? "!=" : "==", buf2);
+ break;
+
+ case PFO_BE: // !a
+ // note: must be unsigned compare
+ snprintf(buf, buf_size, "(%s %s %s)",
+ buf1, is_inv ? ">" : "<=", buf2);
+
+ // annoying case
+ if (is_inv && lmod == OPLM_BYTE
+ && po->operand[1].type == OPT_CONST
+ && po->operand[1].val == 0xff)
+ {
+ snprintf(g_comment, sizeof(g_comment), "if %s", buf);
+ snprintf(buf, buf_size, "(0)");
+ }
+ break;
+
+ // note: must be signed compare
+ case PFO_S:
+ snprintf(buf, buf_size, "(%s(%s - %s) %s 0)",
+ scast, buf1, buf2, is_inv ? ">=" : "<");
+ break;
+
+ case PFO_L: // !ge
+ snprintf(buf, buf_size, "(%s %s %s)",
+ buf1, is_inv ? ">=" : "<", buf2);
+ break;
+
+ case PFO_LE:
+ snprintf(buf, buf_size, "(%s %s %s)",
+ buf1, is_inv ? ">" : "<=", buf2);