+ if (branched || scan_for_mod(tmp_op, setters[j] + 1, i, 0) >= 0)
+ pfomask = 1 << po->pfo;
+ }
+ else if (tmp_op->op == OP_CMPS || tmp_op->op == OP_SCAS) {
+ pfomask = 1 << po->pfo;
+ }
+ else {
+ // see if we'll be able to handle based on op result
+ if ((tmp_op->op != OP_AND && tmp_op->op != OP_OR
+ && po->pfo != PFO_Z && po->pfo != PFO_S
+ && po->pfo != PFO_P)
+ || branched
+ || scan_for_mod_opr0(tmp_op, setters[j] + 1, i) >= 0)
+ {
+ pfomask = 1 << po->pfo;
+ }
+
+ if (tmp_op->op == OP_ADD && po->pfo == PFO_C) {
+ propagate_lmod(tmp_op, &tmp_op->operand[0],
+ &tmp_op->operand[1]);
+ if (tmp_op->operand[0].lmod == OPLM_DWORD)
+ need_tmp64 = 1;
+ }
+ }
+ if (pfomask) {
+ tmp_op->pfomask |= pfomask;
+ cond_vars |= pfomask;
+ }
+ // note: may overwrite, currently not a problem
+ po->datap = tmp_op;
+ }
+
+ if (po->op == OP_RCL || po->op == OP_RCR
+ || po->op == OP_ADC || po->op == OP_SBB)
+ cond_vars |= 1 << PFO_C;
+ }
+
+ if (po->op == OP_CMPS || po->op == OP_SCAS) {
+ cond_vars |= 1 << PFO_Z;
+ }
+ else if (po->op == OP_MUL
+ || (po->op == OP_IMUL && po->operand_cnt == 1))
+ {
+ if (po->operand[0].lmod == OPLM_DWORD)
+ need_tmp64 = 1;
+ }
+ else if (po->op == OP_CALL) {
+ // note: resolved non-reg calls are OPF_DONE already
+ pp = po->pp;
+ if (pp == NULL)
+ ferr(po, "NULL pp\n");
+
+ if (pp->is_unresolved) {
+ int regmask_stack = 0;
+ collect_call_args(po, i, pp, ®mask, save_arg_vars,
+ i + opcnt * 2);
+
+ // this is pretty rough guess:
+ // see ecx and edx were pushed (and not their saved versions)
+ for (arg = 0; arg < pp->argc; arg++) {
+ if (pp->arg[arg].reg != NULL)
+ continue;
+
+ tmp_op = pp->arg[arg].datap;
+ if (tmp_op == NULL)
+ ferr(po, "parsed_op missing for arg%d\n", arg);
+ if (tmp_op->p_argnum == 0 && tmp_op->operand[0].type == OPT_REG)
+ regmask_stack |= 1 << tmp_op->operand[0].reg;
+ }
+
+ if (!((regmask_stack & (1 << xCX))
+ && (regmask_stack & (1 << xDX))))
+ {
+ if (pp->argc_stack != 0
+ || ((regmask | regmask_arg) & ((1 << xCX)|(1 << xDX))))
+ {
+ pp_insert_reg_arg(pp, "ecx");
+ pp->is_fastcall = 1;
+ regmask_init |= 1 << xCX;
+ regmask |= 1 << xCX;
+ }
+ if (pp->argc_stack != 0
+ || ((regmask | regmask_arg) & (1 << xDX)))
+ {
+ pp_insert_reg_arg(pp, "edx");
+ regmask_init |= 1 << xDX;
+ regmask |= 1 << xDX;
+ }
+ }
+
+ // note: __cdecl doesn't fall into is_unresolved category
+ if (pp->argc_stack > 0)
+ pp->is_stdcall = 1;
+ }
+
+ for (arg = 0; arg < pp->argc; arg++) {
+ if (pp->arg[arg].reg != NULL) {
+ reg = char_array_i(regs_r32,
+ ARRAY_SIZE(regs_r32), pp->arg[arg].reg);
+ if (reg < 0)
+ ferr(ops, "arg '%s' is not a reg?\n", pp->arg[arg].reg);
+ if (!(regmask & (1 << reg))) {
+ regmask_init |= 1 << reg;
+ regmask |= 1 << reg;
+ }
+ }
+ }
+ }
+ else if (po->op == OP_MOV && po->operand[0].pp != NULL
+ && po->operand[1].pp != NULL)
+ {
+ // <var> = offset <something>
+ if ((po->operand[1].pp->is_func || po->operand[1].pp->is_fptr)
+ && !IS_START(po->operand[1].name, "off_"))
+ {
+ if (!po->operand[0].pp->is_fptr)
+ ferr(po, "%s not declared as fptr when it should be\n",
+ po->operand[0].name);
+ if (pp_cmp_func(po->operand[0].pp, po->operand[1].pp)) {
+ pp_print(buf1, sizeof(buf1), po->operand[0].pp);
+ pp_print(buf2, sizeof(buf2), po->operand[1].pp);
+ fnote(po, "var: %s\n", buf1);
+ fnote(po, "func: %s\n", buf2);
+ ferr(po, "^ mismatch\n");
+ }
+ }
+ }
+ else if (po->op == OP_RET && !IS(g_func_pp->ret_type.name, "void"))
+ regmask |= 1 << xAX;
+ else if (po->op == OP_DIV || po->op == OP_IDIV) {
+ // 32bit division is common, look for it
+ if (po->op == OP_DIV)
+ ret = scan_for_reg_clear(i, xDX);
+ else
+ ret = scan_for_cdq_edx(i);
+ if (ret >= 0)
+ po->flags |= OPF_32BIT;
+ else
+ need_tmp64 = 1;
+ }
+ else if (po->op == OP_CLD)
+ po->flags |= OPF_RMD | OPF_DONE;
+
+ if (po->op == OP_RCL || po->op == OP_RCR || po->op == OP_XCHG) {
+ need_tmp_var = 1;
+ }
+ }
+
+ // pass7:
+ // - confirm regmask_save, it might have been reduced
+ if (regmask_save != 0)
+ {
+ regmask_save = 0;
+ for (i = 0; i < opcnt; i++) {
+ po = &ops[i];
+ if (po->flags & OPF_RMD)
+ continue;
+
+ if (po->op == OP_PUSH && (po->flags & OPF_RSAVE))
+ regmask_save |= 1 << po->operand[0].reg;
+ }
+ }
+
+ // output starts here
+
+ // define userstack size
+ if (g_func_pp->is_userstack) {
+ fprintf(fout, "#ifndef US_SZ_%s\n", g_func_pp->name);
+ fprintf(fout, "#define US_SZ_%s USERSTACK_SIZE\n", g_func_pp->name);
+ fprintf(fout, "#endif\n");
+ }
+
+ // the function itself
+ ferr_assert(ops, !g_func_pp->is_fptr);
+ output_pp(fout, g_func_pp,
+ (g_ida_func_attr & IDAFA_NORETURN) ? OPP_FORCE_NORETURN : 0);
+ fprintf(fout, "\n{\n");
+
+ // declare indirect functions
+ for (i = 0; i < opcnt; i++) {
+ po = &ops[i];
+ if (po->flags & OPF_RMD)
+ continue;
+
+ if (po->op == OP_CALL) {
+ pp = po->pp;
+ if (pp == NULL)
+ ferr(po, "NULL pp\n");
+
+ if (pp->is_fptr && !(pp->name[0] != 0 && pp->is_arg)) {
+ if (pp->name[0] != 0) {
+ memmove(pp->name + 2, pp->name, strlen(pp->name) + 1);
+ memcpy(pp->name, "i_", 2);
+
+ // might be declared already
+ found = 0;
+ for (j = 0; j < i; j++) {
+ if (ops[j].op == OP_CALL && (pp_tmp = ops[j].pp)) {
+ if (pp_tmp->is_fptr && IS(pp->name, pp_tmp->name)) {
+ found = 1;
+ break;
+ }
+ }
+ }
+ if (found)
+ continue;
+ }
+ else
+ snprintf(pp->name, sizeof(pp->name), "icall%d", i);
+
+ fprintf(fout, " ");
+ output_pp(fout, pp, OPP_SIMPLE_ARGS);
+ fprintf(fout, ";\n");
+ }
+ }
+ }
+
+ // output LUTs/jumptables
+ for (i = 0; i < g_func_pd_cnt; i++) {
+ pd = &g_func_pd[i];
+ fprintf(fout, " static const ");
+ if (pd->type == OPT_OFFSET) {
+ fprintf(fout, "void *jt_%s[] =\n { ", pd->label);
+
+ for (j = 0; j < pd->count; j++) {
+ if (j > 0)
+ fprintf(fout, ", ");
+ fprintf(fout, "&&%s", pd->d[j].u.label);
+ }
+ }
+ else {
+ fprintf(fout, "%s %s[] =\n { ",
+ lmod_type_u(ops, pd->lmod), pd->label);
+
+ for (j = 0; j < pd->count; j++) {
+ if (j > 0)
+ fprintf(fout, ", ");
+ fprintf(fout, "%u", pd->d[j].u.val);
+ }
+ }
+ fprintf(fout, " };\n");
+ had_decl = 1;
+ }
+
+ // declare stack frame, va_arg
+ if (g_stack_fsz) {
+ fprintf(fout, " union { u32 d[%d]; u16 w[%d]; u8 b[%d]; } sf;\n",
+ (g_stack_fsz + 3) / 4, (g_stack_fsz + 1) / 2, g_stack_fsz);
+ had_decl = 1;
+ }
+
+ if (g_func_pp->is_userstack) {
+ fprintf(fout, " u32 fake_sf[US_SZ_%s / 4];\n", g_func_pp->name);
+ fprintf(fout, " u32 *esp = &fake_sf[sizeof(fake_sf) / 4];\n");
+ had_decl = 1;
+ }
+
+ if (g_func_pp->is_vararg) {
+ fprintf(fout, " va_list ap;\n");
+ had_decl = 1;
+ }
+
+ // declare arg-registers
+ for (i = 0; i < g_func_pp->argc; i++) {
+ if (g_func_pp->arg[i].reg != NULL) {
+ reg = char_array_i(regs_r32,
+ ARRAY_SIZE(regs_r32), g_func_pp->arg[i].reg);
+ if (regmask & (1 << reg)) {
+ if (g_func_pp->arg[i].type.is_retreg)
+ fprintf(fout, " u32 %s = *r_%s;\n",
+ g_func_pp->arg[i].reg, g_func_pp->arg[i].reg);
+ else
+ fprintf(fout, " u32 %s = (u32)a%d;\n",
+ g_func_pp->arg[i].reg, i + 1);
+ }
+ else {
+ if (g_func_pp->arg[i].type.is_retreg)
+ ferr(ops, "retreg '%s' is unused?\n",
+ g_func_pp->arg[i].reg);
+ fprintf(fout, " // %s = a%d; // unused\n",
+ g_func_pp->arg[i].reg, i + 1);
+ }
+ had_decl = 1;
+ }
+ }
+
+ // declare normal registers
+ regmask_now = regmask & ~regmask_arg;
+ regmask_now &= ~(1 << xSP);
+ if (regmask_now & 0x00ff) {
+ for (reg = 0; reg < 8; reg++) {
+ if (regmask_now & (1 << reg)) {
+ fprintf(fout, " u32 %s", regs_r32[reg]);
+ if (regmask_init & (1 << reg))
+ fprintf(fout, " = 0");
+ fprintf(fout, ";\n");
+ had_decl = 1;
+ }
+ }
+ }
+ 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++) {
+ if (regmask_save & (1 << reg)) {
+ fprintf(fout, " u32 s_%s;\n", regs_r32[reg]);
+ had_decl = 1;
+ }
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(save_arg_vars); i++) {
+ if (save_arg_vars[i] == 0)
+ continue;
+ for (reg = 0; reg < 32; reg++) {
+ if (save_arg_vars[i] & (1 << reg)) {
+ fprintf(fout, " u32 %s;\n",
+ saved_arg_name(buf1, sizeof(buf1), i, reg + 1));
+ had_decl = 1;
+ }
+ }
+ }
+
+ // declare push-pop temporaries
+ if (regmask_pp) {
+ for (reg = 0; reg < 8; reg++) {
+ if (regmask_pp & (1 << reg)) {
+ fprintf(fout, " u32 pp_%s;\n", regs_r32[reg]);
+ had_decl = 1;
+ }
+ }
+ }
+
+ if (cond_vars) {
+ for (i = 0; i < 8; i++) {
+ if (cond_vars & (1 << i)) {
+ fprintf(fout, " u32 cond_%s;\n", parsed_flag_op_names[i]);
+ had_decl = 1;
+ }
+ }
+ }
+
+ if (need_tmp_var) {
+ fprintf(fout, " u32 tmp;\n");
+ had_decl = 1;
+ }
+
+ if (need_tmp64) {
+ fprintf(fout, " u64 tmp64;\n");
+ had_decl = 1;
+ }
+
+ if (had_decl)
+ fprintf(fout, "\n");
+
+ if (g_func_pp->is_vararg) {
+ if (g_func_pp->argc_stack == 0)
+ ferr(ops, "vararg func without stack args?\n");
+ fprintf(fout, " va_start(ap, a%d);\n", g_func_pp->argc);
+ }
+
+ // output ops
+ for (i = 0; i < opcnt; i++)
+ {
+ if (g_labels[i] != NULL) {
+ fprintf(fout, "\n%s:\n", g_labels[i]);
+ label_pending = 1;
+
+ delayed_flag_op = NULL;
+ last_arith_dst = NULL;
+ }
+
+ po = &ops[i];
+ if (po->flags & OPF_RMD)
+ continue;
+
+ no_output = 0;
+
+ #define assert_operand_cnt(n_) \
+ if (po->operand_cnt != n_) \
+ ferr(po, "operand_cnt is %d/%d\n", po->operand_cnt, n_)
+
+ // conditional/flag using op?
+ if (po->flags & OPF_CC)
+ {
+ int is_delayed = 0;
+
+ tmp_op = po->datap;
+
+ // we go through all this trouble to avoid using parsed_flag_op,
+ // which makes generated code much nicer
+ if (delayed_flag_op != NULL)
+ {
+ out_cmp_test(buf1, sizeof(buf1), delayed_flag_op,
+ po->pfo, po->pfo_inv);
+ is_delayed = 1;
+ }
+ else if (last_arith_dst != NULL
+ && (po->pfo == PFO_Z || po->pfo == PFO_S || po->pfo == PFO_P
+ || (tmp_op && (tmp_op->op == OP_AND || tmp_op->op == OP_OR))
+ ))
+ {
+ out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst);
+ out_test_for_cc(buf1, sizeof(buf1), po, po->pfo, po->pfo_inv,
+ last_arith_dst->lmod, buf3);
+ is_delayed = 1;
+ }
+ else if (tmp_op != NULL) {
+ // use preprocessed flag calc results
+ if (!(tmp_op->pfomask & (1 << po->pfo)))
+ ferr(po, "not prepared for pfo %d\n", po->pfo);
+
+ // note: pfo_inv was not yet applied
+ snprintf(buf1, sizeof(buf1), "(%scond_%s)",
+ po->pfo_inv ? "!" : "", parsed_flag_op_names[po->pfo]);
+ }
+ else {
+ ferr(po, "all methods of finding comparison failed\n");
+ }
+
+ if (po->flags & OPF_JMP) {
+ fprintf(fout, " if %s", buf1);
+ }
+ else if (po->op == OP_RCL || po->op == OP_RCR
+ || po->op == OP_ADC || po->op == OP_SBB)
+ {
+ if (is_delayed)
+ fprintf(fout, " cond_%s = %s;\n",
+ parsed_flag_op_names[po->pfo], buf1);
+ }
+ else if (po->flags & OPF_DATA) { // SETcc
+ out_dst_opr(buf2, sizeof(buf2), po, &po->operand[0]);
+ fprintf(fout, " %s = %s;", buf2, buf1);
+ }
+ else {
+ ferr(po, "unhandled conditional op\n");
+ }
+ }
+
+ pfomask = po->pfomask;
+
+ if (po->flags & (OPF_REPZ|OPF_REPNZ)) {
+ struct parsed_opr opr = {0,};
+ opr.type = OPT_REG;
+ opr.reg = xCX;
+ opr.lmod = OPLM_DWORD;
+ ret = try_resolve_const(i, &opr, opcnt * 7 + i, &uval);
+
+ if (ret != 1 || uval == 0) {
+ // we need initial flags for ecx=0 case..
+ if (i > 0 && ops[i - 1].op == OP_XOR
+ && IS(ops[i - 1].operand[0].name,
+ ops[i - 1].operand[1].name))
+ {
+ fprintf(fout, " cond_z = ");
+ if (pfomask & (1 << PFO_C))
+ fprintf(fout, "cond_c = ");
+ fprintf(fout, "0;\n");
+ }
+ else if (last_arith_dst != NULL) {
+ out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst);
+ out_test_for_cc(buf1, sizeof(buf1), po, PFO_Z, 0,
+ last_arith_dst->lmod, buf3);
+ fprintf(fout, " cond_z = %s;\n", buf1);
+ }
+ else
+ ferr(po, "missing initial ZF\n");
+ }
+ }
+
+ switch (po->op)
+ {
+ case OP_MOV:
+ assert_operand_cnt(2);
+ propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ default_cast_to(buf3, sizeof(buf3), &po->operand[0]);
+ fprintf(fout, " %s = %s;", buf1,
+ out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
+ buf3, 0));
+ break;
+
+ case OP_LEA:
+ assert_operand_cnt(2);
+ po->operand[1].lmod = OPLM_DWORD; // always
+ fprintf(fout, " %s = %s;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
+ out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
+ NULL, 1));
+ break;
+
+ case OP_MOVZX:
+ assert_operand_cnt(2);
+ fprintf(fout, " %s = %s;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+ break;
+
+ case OP_MOVSX:
+ assert_operand_cnt(2);
+ switch (po->operand[1].lmod) {
+ case OPLM_BYTE:
+ strcpy(buf3, "(s8)");
+ break;
+ case OPLM_WORD:
+ strcpy(buf3, "(s16)");
+ break;
+ default:
+ ferr(po, "invalid src lmod: %d\n", po->operand[1].lmod);
+ }
+ fprintf(fout, " %s = %s;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
+ out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
+ buf3, 0));
+ break;
+
+ case OP_XCHG:
+ assert_operand_cnt(2);
+ propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ fprintf(fout, " tmp = %s;",
+ out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], "", 0));
+ fprintf(fout, " %s = %s;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
+ out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
+ default_cast_to(buf3, sizeof(buf3), &po->operand[0]), 0));
+ fprintf(fout, " %s = %stmp;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[1]),
+ default_cast_to(buf3, sizeof(buf3), &po->operand[1]));
+ snprintf(g_comment, sizeof(g_comment), "xchg");
+ break;
+
+ case OP_NOT:
+ assert_operand_cnt(1);
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ fprintf(fout, " %s = ~%s;", buf1, buf1);
+ break;
+
+ case OP_XLAT:
+ assert_operand_cnt(2);
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
+ fprintf(fout, " %s = *(u8 *)(%s + %s);", buf1, buf2, buf1);
+ strcpy(g_comment, "xlat");
+ break;
+
+ case OP_CDQ:
+ assert_operand_cnt(2);
+ fprintf(fout, " %s = (s32)%s >> 31;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+ strcpy(g_comment, "cdq");
+ break;
+
+ case OP_LODS:
+ assert_operand_cnt(3);
+ if (po->flags & OPF_REP) {
+ // hmh..
+ ferr(po, "TODO\n");
+ }
+ else {
+ fprintf(fout, " eax = %sesi; esi %c= %d;",
+ lmod_cast_u_ptr(po, po->operand[0].lmod),
+ (po->flags & OPF_DF) ? '-' : '+',
+ lmod_bytes(po, po->operand[0].lmod));
+ strcpy(g_comment, "lods");
+ }
+ break;
+
+ case OP_STOS:
+ assert_operand_cnt(3);
+ if (po->flags & OPF_REP) {
+ fprintf(fout, " for (; ecx != 0; ecx--, edi %c= %d)\n",
+ (po->flags & OPF_DF) ? '-' : '+',
+ lmod_bytes(po, po->operand[0].lmod));
+ fprintf(fout, " %sedi = eax;",
+ lmod_cast_u_ptr(po, po->operand[0].lmod));
+ strcpy(g_comment, "rep stos");
+ }
+ else {
+ fprintf(fout, " %sedi = eax; edi %c= %d;",
+ lmod_cast_u_ptr(po, po->operand[0].lmod),
+ (po->flags & OPF_DF) ? '-' : '+',
+ lmod_bytes(po, po->operand[0].lmod));
+ strcpy(g_comment, "stos");
+ }
+ break;
+
+ case OP_MOVS:
+ assert_operand_cnt(3);
+ j = lmod_bytes(po, po->operand[0].lmod);
+ strcpy(buf1, lmod_cast_u_ptr(po, po->operand[0].lmod));
+ l = (po->flags & OPF_DF) ? '-' : '+';
+ if (po->flags & OPF_REP) {
+ fprintf(fout,
+ " for (; ecx != 0; ecx--, edi %c= %d, esi %c= %d)\n",
+ l, j, l, j);
+ fprintf(fout,
+ " %sedi = %sesi;", buf1, buf1);
+ strcpy(g_comment, "rep movs");
+ }
+ else {
+ fprintf(fout, " %sedi = %sesi; edi %c= %d; esi %c= %d;",
+ buf1, buf1, l, j, l, j);
+ strcpy(g_comment, "movs");
+ }
+ break;
+
+ case OP_CMPS:
+ // repe ~ repeat while ZF=1
+ assert_operand_cnt(3);
+ j = lmod_bytes(po, po->operand[0].lmod);
+ strcpy(buf1, lmod_cast_u_ptr(po, po->operand[0].lmod));
+ l = (po->flags & OPF_DF) ? '-' : '+';
+ if (po->flags & OPF_REP) {
+ fprintf(fout,
+ " for (; ecx != 0; ecx--) {\n");
+ if (pfomask & (1 << PFO_C)) {
+ // ugh..
+ fprintf(fout,
+ " cond_c = %sesi < %sedi;\n", buf1, buf1);
+ pfomask &= ~(1 << PFO_C);
+ }
+ fprintf(fout,
+ " cond_z = (%sesi == %sedi); esi %c= %d, edi %c= %d;\n",
+ buf1, buf1, l, j, l, j);
+ fprintf(fout,
+ " if (cond_z %s 0) break;\n",
+ (po->flags & OPF_REPZ) ? "==" : "!=");
+ fprintf(fout,
+ " }");
+ snprintf(g_comment, sizeof(g_comment), "rep%s cmps",
+ (po->flags & OPF_REPZ) ? "e" : "ne");
+ }
+ else {
+ fprintf(fout,
+ " cond_z = (%sesi == %sedi); esi %c= %d; edi %c= %d;",
+ buf1, buf1, l, j, l, j);
+ strcpy(g_comment, "cmps");
+ }
+ pfomask &= ~(1 << PFO_Z);
+ last_arith_dst = NULL;
+ delayed_flag_op = NULL;
+ break;
+
+ case OP_SCAS:
+ // only does ZF (for now)
+ // repe ~ repeat while ZF=1
+ assert_operand_cnt(3);
+ j = lmod_bytes(po, po->operand[0].lmod);
+ l = (po->flags & OPF_DF) ? '-' : '+';
+ if (po->flags & OPF_REP) {
+ fprintf(fout,
+ " for (; ecx != 0; ecx--) {\n");
+ fprintf(fout,
+ " cond_z = (%seax == %sedi); edi %c= %d;\n",
+ lmod_cast_u(po, po->operand[0].lmod),
+ lmod_cast_u_ptr(po, po->operand[0].lmod), l, j);
+ fprintf(fout,
+ " if (cond_z %s 0) break;\n",
+ (po->flags & OPF_REPZ) ? "==" : "!=");
+ fprintf(fout,
+ " }");
+ snprintf(g_comment, sizeof(g_comment), "rep%s scas",
+ (po->flags & OPF_REPZ) ? "e" : "ne");
+ }
+ else {
+ fprintf(fout, " cond_z = (%seax == %sedi); edi %c= %d;",
+ lmod_cast_u(po, po->operand[0].lmod),
+ lmod_cast_u_ptr(po, po->operand[0].lmod), l, j);
+ strcpy(g_comment, "scas");
+ }
+ pfomask &= ~(1 << PFO_Z);
+ last_arith_dst = NULL;
+ delayed_flag_op = NULL;
+ break;
+
+ // arithmetic w/flags
+ case OP_AND:
+ if (po->operand[1].type == OPT_CONST && !po->operand[1].val)
+ goto dualop_arith_const;
+ propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ goto dualop_arith;
+
+ case OP_OR:
+ propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ if (po->operand[1].type == OPT_CONST) {
+ j = lmod_bytes(po, po->operand[0].lmod);
+ if (((1ull << j * 8) - 1) == po->operand[1].val)
+ goto dualop_arith_const;
+ }
+ goto dualop_arith;
+
+ dualop_arith:
+ assert_operand_cnt(2);
+ fprintf(fout, " %s %s= %s;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
+ op_to_c(po),
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+ output_std_flags(fout, po, &pfomask, buf1);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ break;
+
+ dualop_arith_const:
+ // and 0, or ~0 used instead mov
+ assert_operand_cnt(2);
+ fprintf(fout, " %s = %s;",
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
+ out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
+ default_cast_to(buf3, sizeof(buf3), &po->operand[0]), 0));
+ output_std_flags(fout, po, &pfomask, buf1);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ break;
+
+ case OP_SHL:
+ case OP_SHR:
+ assert_operand_cnt(2);
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ if (pfomask & (1 << PFO_C)) {
+ if (po->operand[1].type == OPT_CONST) {
+ l = lmod_bytes(po, po->operand[0].lmod) * 8;
+ j = po->operand[1].val;
+ j %= l;
+ if (j != 0) {
+ if (po->op == OP_SHL)
+ j = l - j;
+ else
+ j -= 1;
+ fprintf(fout, " cond_c = (%s >> %d) & 1;\n",
+ buf1, j);
+ }
+ else
+ ferr(po, "zero shift?\n");
+ }
+ else
+ ferr(po, "TODO\n");
+ pfomask &= ~(1 << PFO_C);
+ }
+ fprintf(fout, " %s %s= %s", buf1, op_to_c(po),
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+ if (po->operand[1].type != OPT_CONST)
+ fprintf(fout, " & 0x1f");
+ fprintf(fout, ";");
+ output_std_flags(fout, po, &pfomask, buf1);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ break;
+
+ case OP_SAR:
+ assert_operand_cnt(2);
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ fprintf(fout, " %s = %s%s >> %s;", buf1,
+ lmod_cast_s(po, po->operand[0].lmod), buf1,
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
+ output_std_flags(fout, po, &pfomask, buf1);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ break;
+
+ case OP_SHLD:
+ 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]);
+ if (po->operand[2].type != OPT_CONST)
+ ferr(po, "TODO: masking\n");
+ if (po->op == OP_SHLD) {
+ fprintf(fout, " %s <<= %s; %s |= %s >> (%d - %s);",
+ buf1, buf3, buf1, buf2, l, buf3);
+ strcpy(g_comment, "shld");
+ }
+ else {
+ 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);
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ if (po->operand[1].type == OPT_CONST) {
+ j = po->operand[1].val;
+ j %= lmod_bytes(po, po->operand[0].lmod) * 8;
+ fprintf(fout, po->op == OP_ROL ?
+ " %s = (%s << %d) | (%s >> %d);" :
+ " %s = (%s >> %d) | (%s << %d);",
+ buf1, buf1, j, buf1,
+ lmod_bytes(po, po->operand[0].lmod) * 8 - j);