translate: handle more cases with args
[ia32rtools.git] / tools / translate.c
1 /*
2  * ia32rtools
3  * (C) notaz, 2013,2014
4  *
5  * This work is licensed under the terms of 3-clause BSD license.
6  * See COPYING file in the top-level directory.
7  */
8
9 #define _GNU_SOURCE
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13
14 #include "my_assert.h"
15 #include "my_str.h"
16
17 #define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0]))
18 #define IS(w, y) !strcmp(w, y)
19 #define IS_START(w, y) !strncmp(w, y, strlen(y))
20
21 #include "protoparse.h"
22
23 static const char *asmfn;
24 static int asmln;
25 static FILE *g_fhdr;
26
27 #define anote(fmt, ...) \
28         printf("%s:%d: note: " fmt, asmfn, asmln, ##__VA_ARGS__)
29 #define awarn(fmt, ...) \
30         printf("%s:%d: warning: " fmt, asmfn, asmln, ##__VA_ARGS__)
31 #define aerr(fmt, ...) do { \
32         printf("%s:%d: error: " fmt, asmfn, asmln, ##__VA_ARGS__); \
33   fcloseall(); \
34         exit(1); \
35 } while (0)
36
37 #include "masm_tools.h"
38
39 enum op_flags {
40   OPF_RMD    = (1 << 0), /* removed or optimized out */
41   OPF_DATA   = (1 << 1), /* data processing - writes to dst opr */
42   OPF_FLAGS  = (1 << 2), /* sets flags */
43   OPF_JMP    = (1 << 3), /* branch, call */
44   OPF_CJMP   = (1 << 4), /* cond. branch (cc or jecxz) */
45   OPF_CC     = (1 << 5), /* uses flags */
46   OPF_TAIL   = (1 << 6), /* ret or tail call */
47   OPF_RSAVE  = (1 << 7), /* push/pop is local reg save/load */
48   OPF_REP    = (1 << 8), /* prefixed by rep */
49   OPF_REPZ   = (1 << 9), /* rep is repe/repz */
50   OPF_REPNZ  = (1 << 10), /* rep is repne/repnz */
51   OPF_FARG   = (1 << 11), /* push collected as func arg (no reuse) */
52   OPF_EBP_S  = (1 << 12), /* ebp used as scratch here, not BP */
53   OPF_DF     = (1 << 13), /* DF flag set */
54   OPF_ATAIL  = (1 << 14), /* tail call with reused arg frame */
55   OPF_32BIT  = (1 << 15), /* 32bit division */
56   OPF_LOCK   = (1 << 16), /* op has lock prefix */
57   OPF_VAPUSH = (1 << 17), /* vararg ptr push (as call arg) */
58 };
59
60 enum op_op {
61         OP_INVAL,
62         OP_NOP,
63         OP_PUSH,
64         OP_POP,
65         OP_LEAVE,
66         OP_MOV,
67         OP_LEA,
68         OP_MOVZX,
69         OP_MOVSX,
70         OP_XCHG,
71         OP_NOT,
72         OP_CDQ,
73         OP_LODS,
74         OP_STOS,
75         OP_MOVS,
76         OP_CMPS,
77         OP_SCAS,
78         OP_STD,
79         OP_CLD,
80         OP_RET,
81         OP_ADD,
82         OP_SUB,
83         OP_AND,
84         OP_OR,
85         OP_XOR,
86         OP_SHL,
87         OP_SHR,
88         OP_SAR,
89         OP_ROL,
90         OP_ROR,
91         OP_RCL,
92         OP_RCR,
93         OP_ADC,
94         OP_SBB,
95         OP_BSF,
96         OP_INC,
97         OP_DEC,
98         OP_NEG,
99         OP_MUL,
100         OP_IMUL,
101         OP_DIV,
102         OP_IDIV,
103         OP_TEST,
104         OP_CMP,
105         OP_CALL,
106         OP_JMP,
107         OP_JECXZ,
108         OP_JCC,
109         OP_SCC,
110 };
111
112 enum opr_type {
113   OPT_UNSPEC,
114   OPT_REG,
115   OPT_REGMEM,
116   OPT_LABEL,
117   OPT_OFFSET,
118   OPT_CONST,
119 };
120
121 // must be sorted (larger len must be further in enum)
122 enum opr_lenmod {
123         OPLM_UNSPEC,
124         OPLM_BYTE,
125         OPLM_WORD,
126         OPLM_DWORD,
127 };
128
129 #define MAX_OPERANDS 3
130
131 struct parsed_opr {
132   enum opr_type type;
133   enum opr_lenmod lmod;
134   unsigned int is_ptr:1;   // pointer in C
135   unsigned int is_array:1; // array in C
136   unsigned int type_from_var:1; // .. in header, sometimes wrong
137   unsigned int size_mismatch:1; // type override differs from C
138   unsigned int size_lt:1;  // type override is larger than C
139   unsigned int had_ds:1;   // had ds: prefix
140   const struct parsed_proto *pp; // for OPT_LABEL
141   int reg;
142   unsigned int val;
143   char name[112];
144 };
145
146 struct parsed_op {
147   enum op_op op;
148   struct parsed_opr operand[MAX_OPERANDS];
149   unsigned int flags;
150   unsigned char pfo;
151   unsigned char pfo_inv;
152   unsigned char operand_cnt;
153   unsigned char p_argnum; // push: altered before call arg #
154   unsigned char p_argpass;// push: arg of host func
155   unsigned char pad[3];
156   int regmask_src;        // all referensed regs
157   int regmask_dst;
158   int pfomask;            // flagop: parsed_flag_op that can't be delayed
159   int cc_scratch;         // scratch storage during analysis
160   int bt_i;               // branch target for branches
161   struct parsed_data *btj;// branch targets for jumptables
162   struct parsed_proto *pp;// parsed_proto for OP_CALL
163   void *datap;
164   int asmln;
165 };
166
167 // datap:
168 // OP_CALL - parser proto hint (str)
169 // (OPF_CC) - point to one of (OPF_FLAGS) that affects cc op
170 // OP_POP - point to OP_PUSH in push/pop pair
171
172 struct parsed_equ {
173   char name[64];
174   enum opr_lenmod lmod;
175   int offset;
176 };
177
178 struct parsed_data {
179   char label[256];
180   enum opr_type type;
181   enum opr_lenmod lmod;
182   int count;
183   int count_alloc;
184   struct {
185     union {
186       char *label;
187       unsigned int val;
188     } u;
189     int bt_i;
190   } *d;
191 };
192
193 struct label_ref {
194   int i;
195   struct label_ref *next;
196 };
197
198 enum ida_func_attr {
199   IDAFA_BP_FRAME = (1 << 0),
200   IDAFA_LIB_FUNC = (1 << 1),
201   IDAFA_STATIC   = (1 << 2),
202   IDAFA_NORETURN = (1 << 3),
203   IDAFA_THUNK    = (1 << 4),
204   IDAFA_FPD      = (1 << 5),
205 };
206
207 #define MAX_OPS 4096
208
209 static struct parsed_op ops[MAX_OPS];
210 static struct parsed_equ *g_eqs;
211 static int g_eqcnt;
212 static char g_labels[MAX_OPS][48];
213 static struct label_ref g_label_refs[MAX_OPS];
214 static const struct parsed_proto *g_func_pp;
215 static struct parsed_data *g_func_pd;
216 static int g_func_pd_cnt;
217 static char g_func[256];
218 static char g_comment[256];
219 static int g_bp_frame;
220 static int g_sp_frame;
221 static int g_stack_frame_used;
222 static int g_stack_fsz;
223 static int g_ida_func_attr;
224 static int g_allow_regfunc;
225 #define ferr(op_, fmt, ...) do { \
226   printf("%s:%d: error: [%s] '%s': " fmt, asmfn, (op_)->asmln, g_func, \
227     dump_op(op_), ##__VA_ARGS__); \
228   fcloseall(); \
229   exit(1); \
230 } while (0)
231 #define fnote(op_, fmt, ...) \
232   printf("%s:%d: note: [%s] '%s': " fmt, asmfn, (op_)->asmln, g_func, \
233     dump_op(op_), ##__VA_ARGS__)
234
235 #define MAX_REGS 8
236
237 const char *regs_r32[] = { "eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", "esp" };
238 const char *regs_r16[] = { "ax", "bx", "cx", "dx", "si", "di", "bp", "sp" };
239 const char *regs_r8l[] = { "al", "bl", "cl", "dl" };
240 const char *regs_r8h[] = { "ah", "bh", "ch", "dh" };
241
242 enum x86_regs { xUNSPEC = -1, xAX, xBX, xCX, xDX, xSI, xDI, xBP, xSP };
243
244 // possible basic comparison types (without inversion)
245 enum parsed_flag_op {
246   PFO_O,  // 0 OF=1
247   PFO_C,  // 2 CF=1
248   PFO_Z,  // 4 ZF=1
249   PFO_BE, // 6 CF=1||ZF=1
250   PFO_S,  // 8 SF=1
251   PFO_P,  // a PF=1
252   PFO_L,  // c SF!=OF
253   PFO_LE, // e ZF=1||SF!=OF
254 };
255
256 static const char *parsed_flag_op_names[] = {
257   "o", "c", "z", "be", "s", "p", "l", "le"
258 };
259
260 static int char_array_i(const char *array[], size_t len, const char *s)
261 {
262   int i;
263
264   for (i = 0; i < len; i++)
265     if (IS(s, array[i]))
266       return i;
267
268   return -1;
269 }
270
271 static void printf_number(char *buf, size_t buf_size,
272   unsigned long number)
273 {
274   // output in C-friendly form
275   snprintf(buf, buf_size, number < 10 ? "%lu" : "0x%02lx", number);
276 }
277
278 static int check_segment_prefix(const char *s)
279 {
280   if (s[0] == 0 || s[1] != 's' || s[2] != ':')
281     return 0;
282
283   switch (s[0]) {
284   case 'c': return 1;
285   case 'd': return 2;
286   case 's': return 3;
287   case 'e': return 4;
288   case 'f': return 5;
289   case 'g': return 6;
290   default:  return 0;
291   }
292 }
293
294 static int parse_reg(enum opr_lenmod *reg_lmod, const char *s)
295 {
296   int reg;
297
298   reg = char_array_i(regs_r32, ARRAY_SIZE(regs_r32), s);
299   if (reg >= 0) {
300     *reg_lmod = OPLM_DWORD;
301     return reg;
302   }
303   reg = char_array_i(regs_r16, ARRAY_SIZE(regs_r16), s);
304   if (reg >= 0) {
305     *reg_lmod = OPLM_WORD;
306     return reg;
307   }
308   reg = char_array_i(regs_r8h, ARRAY_SIZE(regs_r8h), s);
309   if (reg >= 0) {
310     *reg_lmod = OPLM_BYTE;
311     return reg;
312   }
313   reg = char_array_i(regs_r8l, ARRAY_SIZE(regs_r8l), s);
314   if (reg >= 0) {
315     *reg_lmod = OPLM_BYTE;
316     return reg;
317   }
318
319   return -1;
320 }
321
322 static int parse_indmode(char *name, int *regmask, int need_c_cvt)
323 {
324   enum opr_lenmod lmod;
325   char cvtbuf[256];
326   char *d = cvtbuf;
327   char *s = name;
328   char w[64];
329   long number;
330   int reg;
331   int c = 0;
332
333   *d = 0;
334
335   while (*s != 0) {
336     d += strlen(d);
337     while (my_isblank(*s))
338       s++;
339     for (; my_issep(*s); d++, s++)
340       *d = *s;
341     while (my_isblank(*s))
342       s++;
343     *d = 0;
344
345     // skip '?s:' prefixes
346     if (check_segment_prefix(s))
347       s += 3;
348
349     s = next_idt(w, sizeof(w), s);
350     if (w[0] == 0)
351       break;
352     c++;
353
354     reg = parse_reg(&lmod, w);
355     if (reg >= 0) {
356       *regmask |= 1 << reg;
357       goto pass;
358     }
359
360     if ('0' <= w[0] && w[0] <= '9') {
361       number = parse_number(w);
362       printf_number(d, sizeof(cvtbuf) - (d - cvtbuf), number);
363       continue;
364     }
365
366     // probably some label/identifier - pass
367
368 pass:
369     snprintf(d, sizeof(cvtbuf) - (d - cvtbuf), "%s", w);
370   }
371
372   if (need_c_cvt)
373     strcpy(name, cvtbuf);
374
375   return c;
376 }
377
378 static int is_reg_in_str(const char *s)
379 {
380   int i;
381
382   if (strlen(s) < 3 || (s[3] && !my_issep(s[3]) && !my_isblank(s[3])))
383     return 0;
384
385   for (i = 0; i < ARRAY_SIZE(regs_r32); i++)
386     if (!strncmp(s, regs_r32[i], 3))
387       return 1;
388
389   return 0;
390 }
391
392 static const char *parse_stack_el(const char *name, char *extra_reg,
393   int early_try)
394 {
395   const char *p, *p2, *s;
396   char *endp = NULL;
397   char buf[32];
398   long val;
399   int len;
400
401   if (g_bp_frame || early_try)
402   {
403     p = name;
404     if (IS_START(p + 3, "+ebp+") && is_reg_in_str(p)) {
405       p += 4;
406       if (extra_reg != NULL) {
407         strncpy(extra_reg, name, 3);
408         extra_reg[4] = 0;
409       }
410     }
411
412     if (IS_START(p, "ebp+")) {
413       p += 4;
414
415       p2 = strchr(p, '+');
416       if (p2 != NULL && is_reg_in_str(p)) {
417         if (extra_reg != NULL) {
418           strncpy(extra_reg, p, p2 - p);
419           extra_reg[p2 - p] = 0;
420         }
421         p = p2 + 1;
422       }
423
424       if (!('0' <= *p && *p <= '9'))
425         return p;
426
427       return NULL;
428     }
429   }
430
431   if (!IS_START(name, "esp+"))
432     return NULL;
433
434   s = name + 4;
435   p = strchr(s, '+');
436   if (p) {
437     if (is_reg_in_str(s)) {
438       if (extra_reg != NULL) {
439         strncpy(extra_reg, s, p - s);
440         extra_reg[p - s] = 0;
441       }
442       s = p + 1;
443       p = strchr(s, '+');
444       if (p == NULL)
445         aerr("%s IDA stackvar not set?\n", __func__);
446     }
447     if (!('0' <= *s && *s <= '9')) {
448       aerr("%s IDA stackvar offset not set?\n", __func__);
449       return NULL;
450     }
451     if (s[0] == '0' && s[1] == 'x')
452       s += 2;
453     len = p - s;
454     if (len < sizeof(buf) - 1) {
455       strncpy(buf, s, len);
456       buf[len] = 0;
457       val = strtol(buf, &endp, 16);
458       if (val == 0 || *endp != 0) {
459         aerr("%s num parse fail for '%s'\n", __func__, buf);
460         return NULL;
461       }
462     }
463     p++;
464   }
465   else
466     p = name + 4;
467
468   if ('0' <= *p && *p <= '9')
469     return NULL;
470
471   return p;
472 }
473
474 static int guess_lmod_from_name(struct parsed_opr *opr)
475 {
476   if (!strncmp(opr->name, "dword_", 6)) {
477     opr->lmod = OPLM_DWORD;
478     return 1;
479   }
480   if (!strncmp(opr->name, "word_", 5)) {
481     opr->lmod = OPLM_WORD;
482     return 1;
483   }
484   if (!strncmp(opr->name, "byte_", 5)) {
485     opr->lmod = OPLM_BYTE;
486     return 1;
487   }
488   return 0;
489 }
490
491 static int guess_lmod_from_c_type(enum opr_lenmod *lmod,
492   const struct parsed_type *c_type)
493 {
494   static const char *dword_types[] = {
495     "int", "_DWORD", "UINT_PTR", "DWORD",
496     "WPARAM", "LPARAM", "UINT", "__int32",
497     "LONG", "HIMC", "BOOL", "size_t",
498   };
499   static const char *word_types[] = {
500     "uint16_t", "int16_t", "_WORD", "WORD",
501     "unsigned __int16", "__int16",
502   };
503   static const char *byte_types[] = {
504     "uint8_t", "int8_t", "char",
505     "unsigned __int8", "__int8", "BYTE", "_BYTE",
506     "CHAR", "_UNKNOWN",
507     // structures.. deal the same as with _UNKNOWN for now
508     "CRITICAL_SECTION",
509   };
510   const char *n;
511   int i;
512
513   if (c_type->is_ptr) {
514     *lmod = OPLM_DWORD;
515     return 1;
516   }
517
518   n = skip_type_mod(c_type->name);
519
520   for (i = 0; i < ARRAY_SIZE(dword_types); i++) {
521     if (IS(n, dword_types[i])) {
522       *lmod = OPLM_DWORD;
523       return 1;
524     }
525   }
526
527   for (i = 0; i < ARRAY_SIZE(word_types); i++) {
528     if (IS(n, word_types[i])) {
529       *lmod = OPLM_WORD;
530       return 1;
531     }
532   }
533
534   for (i = 0; i < ARRAY_SIZE(byte_types); i++) {
535     if (IS(n, byte_types[i])) {
536       *lmod = OPLM_BYTE;
537       return 1;
538     }
539   }
540
541   return 0;
542 }
543
544 static char *default_cast_to(char *buf, size_t buf_size,
545   struct parsed_opr *opr)
546 {
547   buf[0] = 0;
548
549   if (!opr->is_ptr)
550     return buf;
551   if (opr->pp == NULL || opr->pp->type.name == NULL
552     || opr->pp->is_fptr)
553   {
554     snprintf(buf, buf_size, "%s", "(void *)");
555     return buf;
556   }
557
558   snprintf(buf, buf_size, "(%s)", opr->pp->type.name);
559   return buf;
560 }
561
562 static enum opr_type lmod_from_directive(const char *d)
563 {
564   if (IS(d, "dd"))
565     return OPLM_DWORD;
566   else if (IS(d, "dw"))
567     return OPLM_WORD;
568   else if (IS(d, "db"))
569     return OPLM_BYTE;
570
571   aerr("unhandled directive: '%s'\n", d);
572   return OPLM_UNSPEC;
573 }
574
575 static void setup_reg_opr(struct parsed_opr *opr, int reg, enum opr_lenmod lmod,
576   int *regmask)
577 {
578   opr->type = OPT_REG;
579   opr->reg = reg;
580   opr->lmod = lmod;
581   *regmask |= 1 << reg;
582 }
583
584 static struct parsed_equ *equ_find(struct parsed_op *po, const char *name,
585   int *extra_offs);
586
587 static int parse_operand(struct parsed_opr *opr,
588   int *regmask, int *regmask_indirect,
589   char words[16][256], int wordc, int w, unsigned int op_flags)
590 {
591   const struct parsed_proto *pp;
592   enum opr_lenmod tmplmod;
593   unsigned long number;
594   char buf[256];
595   int ret, len;
596   int wordc_in;
597   char *p;
598   int i;
599
600   if (w >= wordc)
601     aerr("parse_operand w %d, wordc %d\n", w, wordc);
602
603   opr->reg = xUNSPEC;
604
605   for (i = w; i < wordc; i++) {
606     len = strlen(words[i]);
607     if (words[i][len - 1] == ',') {
608       words[i][len - 1] = 0;
609       wordc = i + 1;
610       break;
611     }
612   }
613
614   wordc_in = wordc - w;
615
616   if ((op_flags & OPF_JMP) && wordc_in > 0
617       && !('0' <= words[w][0] && words[w][0] <= '9'))
618   {
619     const char *label = NULL;
620
621     if (wordc_in == 3 && !strncmp(words[w], "near", 4)
622      && IS(words[w + 1], "ptr"))
623       label = words[w + 2];
624     else if (wordc_in == 2 && IS(words[w], "short"))
625       label = words[w + 1];
626     else if (wordc_in == 1
627           && strchr(words[w], '[') == NULL
628           && parse_reg(&tmplmod, words[w]) < 0)
629       label = words[w];
630
631     if (label != NULL) {
632       opr->type = OPT_LABEL;
633       ret = check_segment_prefix(label);
634       if (ret != 0) {
635         if (ret >= 5)
636           aerr("fs/gs used\n");
637         opr->had_ds = 1;
638         label += 3;
639       }
640       strcpy(opr->name, label);
641       return wordc;
642     }
643   }
644
645   if (wordc_in >= 3) {
646     if (IS(words[w + 1], "ptr")) {
647       if (IS(words[w], "dword"))
648         opr->lmod = OPLM_DWORD;
649       else if (IS(words[w], "word"))
650         opr->lmod = OPLM_WORD;
651       else if (IS(words[w], "byte"))
652         opr->lmod = OPLM_BYTE;
653       else
654         aerr("type parsing failed\n");
655       w += 2;
656       wordc_in = wordc - w;
657     }
658   }
659
660   if (wordc_in == 2) {
661     if (IS(words[w], "offset")) {
662       opr->type = OPT_OFFSET;
663       opr->lmod = OPLM_DWORD;
664       strcpy(opr->name, words[w + 1]);
665       pp = proto_parse(g_fhdr, opr->name, 1);
666       goto do_label;
667     }
668     if (IS(words[w], "(offset")) {
669       p = strchr(words[w + 1], ')');
670       if (p == NULL)
671         aerr("parse of bracketed offset failed\n");
672       *p = 0;
673       opr->type = OPT_OFFSET;
674       strcpy(opr->name, words[w + 1]);
675       return wordc;
676     }
677   }
678
679   if (wordc_in != 1)
680     aerr("parse_operand 1 word expected\n");
681
682   ret = check_segment_prefix(words[w]);
683   if (ret != 0) {
684     if (ret >= 5)
685       aerr("fs/gs used\n");
686     opr->had_ds = 1;
687     memmove(words[w], words[w] + 3, strlen(words[w]) - 2);
688   }
689   strcpy(opr->name, words[w]);
690
691   if (words[w][0] == '[') {
692     opr->type = OPT_REGMEM;
693     ret = sscanf(words[w], "[%[^]]]", opr->name);
694     if (ret != 1)
695       aerr("[] parse failure\n");
696
697     parse_indmode(opr->name, regmask_indirect, 1);
698     if (opr->lmod == OPLM_UNSPEC && parse_stack_el(opr->name, NULL, 1))
699     {
700       // might be an equ
701       struct parsed_equ *eq =
702         equ_find(NULL, parse_stack_el(opr->name, NULL, 1), &i);
703       if (eq)
704         opr->lmod = eq->lmod;
705     }
706     return wordc;
707   }
708   else if (strchr(words[w], '[')) {
709     // label[reg] form
710     p = strchr(words[w], '[');
711     opr->type = OPT_REGMEM;
712     parse_indmode(p, regmask_indirect, 0);
713     strncpy(buf, words[w], p - words[w]);
714     buf[p - words[w]] = 0;
715     pp = proto_parse(g_fhdr, buf, 1);
716     goto do_label;
717   }
718   else if (('0' <= words[w][0] && words[w][0] <= '9')
719     || words[w][0] == '-')
720   {
721     number = parse_number(words[w]);
722     opr->type = OPT_CONST;
723     opr->val = number;
724     printf_number(opr->name, sizeof(opr->name), number);
725     return wordc;
726   }
727
728   ret = parse_reg(&tmplmod, opr->name);
729   if (ret >= 0) {
730     setup_reg_opr(opr, ret, tmplmod, regmask);
731     return wordc;
732   }
733
734   // most likely var in data segment
735   opr->type = OPT_LABEL;
736   pp = proto_parse(g_fhdr, opr->name, 0);
737
738 do_label:
739   if (pp != NULL) {
740     if (pp->is_fptr || pp->is_func) {
741       opr->lmod = OPLM_DWORD;
742       opr->is_ptr = 1;
743     }
744     else {
745       tmplmod = OPLM_UNSPEC;
746       if (!guess_lmod_from_c_type(&tmplmod, &pp->type))
747         anote("unhandled C type '%s' for '%s'\n",
748           pp->type.name, opr->name);
749       
750       if (opr->lmod == OPLM_UNSPEC) {
751         opr->lmod = tmplmod;
752         opr->type_from_var = 1;
753       }
754       else if (opr->lmod != tmplmod) {
755         opr->size_mismatch = 1;
756         if (tmplmod < opr->lmod)
757           opr->size_lt = 1;
758       }
759       opr->is_ptr = pp->type.is_ptr;
760     }
761     opr->is_array = pp->type.is_array;
762   }
763   opr->pp = pp;
764
765   if (opr->lmod == OPLM_UNSPEC)
766     guess_lmod_from_name(opr);
767   return wordc;
768 }
769
770 static const struct {
771   const char *name;
772   unsigned int flags;
773 } pref_table[] = {
774   { "rep",    OPF_REP },
775   { "repe",   OPF_REP|OPF_REPZ },
776   { "repz",   OPF_REP|OPF_REPZ },
777   { "repne",  OPF_REP|OPF_REPNZ },
778   { "repnz",  OPF_REP|OPF_REPNZ },
779   { "lock",   OPF_LOCK }, // ignored for now..
780 };
781
782 #define OPF_CJMP_CC (OPF_JMP|OPF_CJMP|OPF_CC)
783
784 static const struct {
785   const char *name;
786   enum op_op op;
787   unsigned short minopr;
788   unsigned short maxopr;
789   unsigned int flags;
790   unsigned char pfo;
791   unsigned char pfo_inv;
792 } op_table[] = {
793   { "nop",  OP_NOP,    0, 0, 0 },
794   { "push", OP_PUSH,   1, 1, 0 },
795   { "pop",  OP_POP,    1, 1, OPF_DATA },
796   { "leave",OP_LEAVE,  0, 0, OPF_DATA },
797   { "mov" , OP_MOV,    2, 2, OPF_DATA },
798   { "lea",  OP_LEA,    2, 2, OPF_DATA },
799   { "movzx",OP_MOVZX,  2, 2, OPF_DATA },
800   { "movsx",OP_MOVSX,  2, 2, OPF_DATA },
801   { "xchg", OP_XCHG,   2, 2, OPF_DATA },
802   { "not",  OP_NOT,    1, 1, OPF_DATA },
803   { "cdq",  OP_CDQ,    0, 0, OPF_DATA },
804   { "lodsb",OP_LODS,   0, 0, OPF_DATA },
805   { "lodsw",OP_LODS,   0, 0, OPF_DATA },
806   { "lodsd",OP_LODS,   0, 0, OPF_DATA },
807   { "stosb",OP_STOS,   0, 0, OPF_DATA },
808   { "stosw",OP_STOS,   0, 0, OPF_DATA },
809   { "stosd",OP_STOS,   0, 0, OPF_DATA },
810   { "movsb",OP_MOVS,   0, 0, OPF_DATA },
811   { "movsw",OP_MOVS,   0, 0, OPF_DATA },
812   { "movsd",OP_MOVS,   0, 0, OPF_DATA },
813   { "cmpsb",OP_CMPS,   0, 0, OPF_DATA|OPF_FLAGS },
814   { "cmpsw",OP_CMPS,   0, 0, OPF_DATA|OPF_FLAGS },
815   { "cmpsd",OP_CMPS,   0, 0, OPF_DATA|OPF_FLAGS },
816   { "scasb",OP_SCAS,   0, 0, OPF_DATA|OPF_FLAGS },
817   { "scasw",OP_SCAS,   0, 0, OPF_DATA|OPF_FLAGS },
818   { "scasd",OP_SCAS,   0, 0, OPF_DATA|OPF_FLAGS },
819   { "std",  OP_STD,    0, 0, OPF_DATA }, // special flag
820   { "cld",  OP_CLD,    0, 0, OPF_DATA },
821   { "add",  OP_ADD,    2, 2, OPF_DATA|OPF_FLAGS },
822   { "sub",  OP_SUB,    2, 2, OPF_DATA|OPF_FLAGS },
823   { "and",  OP_AND,    2, 2, OPF_DATA|OPF_FLAGS },
824   { "or",   OP_OR,     2, 2, OPF_DATA|OPF_FLAGS },
825   { "xor",  OP_XOR,    2, 2, OPF_DATA|OPF_FLAGS },
826   { "shl",  OP_SHL,    2, 2, OPF_DATA|OPF_FLAGS },
827   { "shr",  OP_SHR,    2, 2, OPF_DATA|OPF_FLAGS },
828   { "sal",  OP_SHL,    2, 2, OPF_DATA|OPF_FLAGS },
829   { "sar",  OP_SAR,    2, 2, OPF_DATA|OPF_FLAGS },
830   { "rol",  OP_ROL,    2, 2, OPF_DATA|OPF_FLAGS },
831   { "ror",  OP_ROR,    2, 2, OPF_DATA|OPF_FLAGS },
832   { "rcl",  OP_RCL,    2, 2, OPF_DATA|OPF_FLAGS|OPF_CC, PFO_C },
833   { "rcr",  OP_RCR,    2, 2, OPF_DATA|OPF_FLAGS|OPF_CC, PFO_C },
834   { "adc",  OP_ADC,    2, 2, OPF_DATA|OPF_FLAGS|OPF_CC, PFO_C },
835   { "sbb",  OP_SBB,    2, 2, OPF_DATA|OPF_FLAGS|OPF_CC, PFO_C },
836   { "bsf",  OP_BSF,    2, 2, OPF_DATA|OPF_FLAGS },
837   { "inc",  OP_INC,    1, 1, OPF_DATA|OPF_FLAGS },
838   { "dec",  OP_DEC,    1, 1, OPF_DATA|OPF_FLAGS },
839   { "neg",  OP_NEG,    1, 1, OPF_DATA|OPF_FLAGS },
840   { "mul",  OP_MUL,    1, 1, OPF_DATA|OPF_FLAGS },
841   { "imul", OP_IMUL,   1, 3, OPF_DATA|OPF_FLAGS },
842   { "div",  OP_DIV,    1, 1, OPF_DATA|OPF_FLAGS },
843   { "idiv", OP_IDIV,   1, 1, OPF_DATA|OPF_FLAGS },
844   { "test", OP_TEST,   2, 2, OPF_FLAGS },
845   { "cmp",  OP_CMP,    2, 2, OPF_FLAGS },
846   { "retn", OP_RET,    0, 1, OPF_TAIL },
847   { "call", OP_CALL,   1, 1, OPF_JMP|OPF_DATA|OPF_FLAGS },
848   { "jmp",  OP_JMP,    1, 1, OPF_JMP },
849   { "jecxz",OP_JECXZ,  1, 1, OPF_JMP|OPF_CJMP },
850   { "jo",   OP_JCC,    1, 1, OPF_CJMP_CC, PFO_O,  0 }, // 70 OF=1
851   { "jno",  OP_JCC,    1, 1, OPF_CJMP_CC, PFO_O,  1 }, // 71 OF=0
852   { "jc",   OP_JCC,    1, 1, OPF_CJMP_CC, PFO_C,  0 }, // 72 CF=1
853   { "jb",   OP_JCC,    1, 1, OPF_CJMP_CC, PFO_C,  0 }, // 72
854   { "jnc",  OP_JCC,    1, 1, OPF_CJMP_CC, PFO_C,  1 }, // 73 CF=0
855   { "jnb",  OP_JCC,    1, 1, OPF_CJMP_CC, PFO_C,  1 }, // 73
856   { "jae",  OP_JCC,    1, 1, OPF_CJMP_CC, PFO_C,  1 }, // 73
857   { "jz",   OP_JCC,    1, 1, OPF_CJMP_CC, PFO_Z,  0 }, // 74 ZF=1
858   { "je",   OP_JCC,    1, 1, OPF_CJMP_CC, PFO_Z,  0 }, // 74
859   { "jnz",  OP_JCC,    1, 1, OPF_CJMP_CC, PFO_Z,  1 }, // 75 ZF=0
860   { "jne",  OP_JCC,    1, 1, OPF_CJMP_CC, PFO_Z,  1 }, // 75
861   { "jbe",  OP_JCC,    1, 1, OPF_CJMP_CC, PFO_BE, 0 }, // 76 CF=1||ZF=1
862   { "jna",  OP_JCC,    1, 1, OPF_CJMP_CC, PFO_BE, 0 }, // 76
863   { "ja",   OP_JCC,    1, 1, OPF_CJMP_CC, PFO_BE, 1 }, // 77 CF=0&&ZF=0
864   { "jnbe", OP_JCC,    1, 1, OPF_CJMP_CC, PFO_BE, 1 }, // 77
865   { "js",   OP_JCC,    1, 1, OPF_CJMP_CC, PFO_S,  0 }, // 78 SF=1
866   { "jns",  OP_JCC,    1, 1, OPF_CJMP_CC, PFO_S,  1 }, // 79 SF=0
867   { "jp",   OP_JCC,    1, 1, OPF_CJMP_CC, PFO_P,  0 }, // 7a PF=1
868   { "jpe",  OP_JCC,    1, 1, OPF_CJMP_CC, PFO_P,  0 }, // 7a
869   { "jnp",  OP_JCC,    1, 1, OPF_CJMP_CC, PFO_P,  1 }, // 7b PF=0
870   { "jpo",  OP_JCC,    1, 1, OPF_CJMP_CC, PFO_P,  1 }, // 7b
871   { "jl",   OP_JCC,    1, 1, OPF_CJMP_CC, PFO_L,  0 }, // 7c SF!=OF
872   { "jnge", OP_JCC,    1, 1, OPF_CJMP_CC, PFO_L,  0 }, // 7c
873   { "jge",  OP_JCC,    1, 1, OPF_CJMP_CC, PFO_L,  1 }, // 7d SF=OF
874   { "jnl",  OP_JCC,    1, 1, OPF_CJMP_CC, PFO_L,  1 }, // 7d
875   { "jle",  OP_JCC,    1, 1, OPF_CJMP_CC, PFO_LE, 0 }, // 7e ZF=1||SF!=OF
876   { "jng",  OP_JCC,    1, 1, OPF_CJMP_CC, PFO_LE, 0 }, // 7e
877   { "jg",   OP_JCC,    1, 1, OPF_CJMP_CC, PFO_LE, 1 }, // 7f ZF=0&&SF=OF
878   { "jnle", OP_JCC,    1, 1, OPF_CJMP_CC, PFO_LE, 1 }, // 7f
879   { "seto",   OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_O,  0 },
880   { "setno",  OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_O,  1 },
881   { "setc",   OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_C,  0 },
882   { "setb",   OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_C,  0 },
883   { "setnc",  OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_C,  1 },
884   { "setae",  OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_C,  1 },
885   { "setnb",  OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_C,  1 },
886   { "setz",   OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_Z,  0 },
887   { "sete",   OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_Z,  0 },
888   { "setnz",  OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_Z,  1 },
889   { "setne",  OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_Z,  1 },
890   { "setbe",  OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_BE, 0 },
891   { "setna",  OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_BE, 0 },
892   { "seta",   OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_BE, 1 },
893   { "setnbe", OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_BE, 1 },
894   { "sets",   OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_S,  0 },
895   { "setns",  OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_S,  1 },
896   { "setp",   OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_P,  0 },
897   { "setpe",  OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_P,  0 },
898   { "setnp",  OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_P,  1 },
899   { "setpo",  OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_P,  1 },
900   { "setl",   OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_L,  0 },
901   { "setnge", OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_L,  0 },
902   { "setge",  OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_L,  1 },
903   { "setnl",  OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_L,  1 },
904   { "setle",  OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_LE, 0 },
905   { "setng",  OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_LE, 0 },
906   { "setg",   OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_LE, 1 },
907   { "setnle", OP_SCC,  1, 1, OPF_DATA|OPF_CC, PFO_LE, 1 },
908 };
909
910 static void parse_op(struct parsed_op *op, char words[16][256], int wordc)
911 {
912   enum opr_lenmod lmod = OPLM_UNSPEC;
913   int prefix_flags = 0;
914   int regmask_ind;
915   int regmask;
916   int op_w = 0;
917   int opr = 0;
918   int w = 0;
919   int i;
920
921   for (i = 0; i < ARRAY_SIZE(pref_table); i++) {
922     if (IS(words[w], pref_table[i].name)) {
923       prefix_flags = pref_table[i].flags;
924       break;
925     }
926   }
927
928   if (prefix_flags) {
929     if (wordc <= 1)
930       aerr("lone prefix: '%s'\n", words[0]);
931     w++;
932   }
933
934   op_w = w;
935   for (i = 0; i < ARRAY_SIZE(op_table); i++) {
936     if (IS(words[w], op_table[i].name))
937       break;
938   }
939
940   if (i == ARRAY_SIZE(op_table))
941     aerr("unhandled op: '%s'\n", words[0]);
942   w++;
943
944   op->op = op_table[i].op;
945   op->flags = op_table[i].flags | prefix_flags;
946   op->pfo = op_table[i].pfo;
947   op->pfo_inv = op_table[i].pfo_inv;
948   op->regmask_src = op->regmask_dst = 0;
949   op->asmln = asmln;
950
951   for (opr = 0; opr < op_table[i].minopr; opr++) {
952     regmask = regmask_ind = 0;
953     w = parse_operand(&op->operand[opr], &regmask, &regmask_ind,
954       words, wordc, w, op->flags);
955
956     if (opr == 0 && (op->flags & OPF_DATA))
957       op->regmask_dst = regmask;
958     // for now, mark dst as src too
959     op->regmask_src |= regmask | regmask_ind;
960   }
961
962   for (; w < wordc && opr < op_table[i].maxopr; opr++) {
963     w = parse_operand(&op->operand[opr],
964       &op->regmask_src, &op->regmask_src,
965       words, wordc, w, op->flags);
966   }
967
968   if (w < wordc)
969     aerr("parse_op %s incomplete: %d/%d\n",
970       words[0], w, wordc);
971
972   // special cases
973   op->operand_cnt = opr;
974   if (!strncmp(op_table[i].name, "set", 3))
975     op->operand[0].lmod = OPLM_BYTE;
976
977   // ops with implicit argumets
978   switch (op->op) {
979   case OP_CDQ:
980     op->operand_cnt = 2;
981     setup_reg_opr(&op->operand[0], xDX, OPLM_DWORD, &op->regmask_dst);
982     setup_reg_opr(&op->operand[1], xAX, OPLM_DWORD, &op->regmask_src);
983     break;
984
985   case OP_LODS:
986   case OP_STOS:
987   case OP_SCAS:
988     if (op->operand_cnt != 0)
989       break;
990     if      (words[op_w][4] == 'b')
991       lmod = OPLM_BYTE;
992     else if (words[op_w][4] == 'w')
993       lmod = OPLM_WORD;
994     else if (words[op_w][4] == 'd')
995       lmod = OPLM_DWORD;
996     op->operand_cnt = 3;
997     setup_reg_opr(&op->operand[0], op->op == OP_LODS ? xSI : xDI,
998       lmod, &op->regmask_src);
999     setup_reg_opr(&op->operand[1], xCX, OPLM_DWORD, &op->regmask_src);
1000     op->regmask_dst = op->regmask_src;
1001     setup_reg_opr(&op->operand[2], xAX, OPLM_DWORD,
1002       op->op == OP_LODS ? &op->regmask_dst : &op->regmask_src);
1003     break;
1004
1005   case OP_MOVS:
1006   case OP_CMPS:
1007     if (op->operand_cnt != 0)
1008       break;
1009     if      (words[op_w][4] == 'b')
1010       lmod = OPLM_BYTE;
1011     else if (words[op_w][4] == 'w')
1012       lmod = OPLM_WORD;
1013     else if (words[op_w][4] == 'd')
1014       lmod = OPLM_DWORD;
1015     op->operand_cnt = 3;
1016     setup_reg_opr(&op->operand[0], xDI, lmod, &op->regmask_src);
1017     setup_reg_opr(&op->operand[1], xSI, OPLM_DWORD, &op->regmask_src);
1018     setup_reg_opr(&op->operand[2], xCX, OPLM_DWORD, &op->regmask_src);
1019     op->regmask_dst = op->regmask_src;
1020     break;
1021
1022   case OP_XCHG:
1023     op->regmask_src |= op->regmask_dst;
1024     op->regmask_dst |= op->regmask_src;
1025     goto check_align;
1026
1027   case OP_JECXZ:
1028     op->operand_cnt = 1;
1029     op->regmask_src = 1 << xCX;
1030     op->operand[0].type = OPT_REG;
1031     op->operand[0].reg = xCX;
1032     op->operand[0].lmod = OPLM_DWORD;
1033     break;
1034
1035   case OP_IMUL:
1036     if (op->operand_cnt != 1)
1037       break;
1038     // fallthrough
1039   case OP_MUL:
1040     // singleop mul
1041     op->regmask_dst = (1 << xDX) | (1 << xAX);
1042     op->regmask_src |= (1 << xAX);
1043     if (op->operand[0].lmod == OPLM_UNSPEC)
1044       op->operand[0].lmod = OPLM_DWORD;
1045     break;
1046
1047   case OP_DIV:
1048   case OP_IDIV:
1049     // we could set up operands for edx:eax, but there is no real need to
1050     // (see is_opr_modified())
1051     regmask = (1 << xDX) | (1 << xAX);
1052     op->regmask_dst = regmask;
1053     op->regmask_src |= regmask;
1054     if (op->operand[0].lmod == OPLM_UNSPEC)
1055       op->operand[0].lmod = OPLM_DWORD;
1056     break;
1057
1058   case OP_SHL:
1059   case OP_SHR:
1060   case OP_SAR:
1061   case OP_ROL:
1062   case OP_ROR:
1063     if (op->operand[1].lmod == OPLM_UNSPEC)
1064       op->operand[1].lmod = OPLM_BYTE;
1065     break;
1066
1067   case OP_PUSH:
1068     if (op->operand[0].lmod == OPLM_UNSPEC
1069         && (op->operand[0].type == OPT_CONST
1070          || op->operand[0].type == OPT_OFFSET
1071          || op->operand[0].type == OPT_LABEL))
1072       op->operand[0].lmod = OPLM_DWORD;
1073     break;
1074
1075   // alignment
1076   case OP_MOV:
1077   check_align:
1078     if (op->operand[0].type == OPT_REG && op->operand[1].type == OPT_REG
1079      && op->operand[0].lmod == op->operand[1].lmod
1080      && op->operand[0].reg == op->operand[1].reg
1081      && IS(op->operand[0].name, op->operand[1].name)) // ah, al..
1082     {
1083       op->flags |= OPF_RMD;
1084       op->regmask_src = op->regmask_dst = 0;
1085     }
1086     break;
1087
1088   case OP_LEA:
1089     if (op->operand[0].type == OPT_REG
1090      && op->operand[1].type == OPT_REGMEM)
1091     {
1092       char buf[16];
1093       snprintf(buf, sizeof(buf), "%s+0", op->operand[0].name);
1094       if (IS(buf, op->operand[1].name))
1095         op->flags |= OPF_RMD;
1096     }
1097     break;
1098
1099   case OP_CALL:
1100     // trashed regs must be explicitly detected later
1101     op->regmask_dst = 0;
1102     break;
1103
1104   default:
1105     break;
1106   }
1107 }
1108
1109 static const char *op_name(struct parsed_op *po)
1110 {
1111   static char buf[16];
1112   char *p;
1113   int i;
1114
1115   if (po->op == OP_JCC || po->op == OP_SCC) {
1116     p = buf;
1117     *p++ = (po->op == OP_JCC) ? 'j' : 's';
1118     if (po->pfo_inv)
1119       *p++ = 'n';
1120     strcpy(p, parsed_flag_op_names[po->pfo]);
1121     return buf;
1122   }
1123
1124   for (i = 0; i < ARRAY_SIZE(op_table); i++)
1125     if (op_table[i].op == po->op)
1126       return op_table[i].name;
1127
1128   return "???";
1129 }
1130
1131 // debug
1132 static const char *dump_op(struct parsed_op *po)
1133 {
1134   static char out[128];
1135   char *p = out;
1136   int i;
1137
1138   if (po == NULL)
1139     return "???";
1140
1141   snprintf(out, sizeof(out), "%s", op_name(po));
1142   for (i = 0; i < po->operand_cnt; i++) {
1143     p += strlen(p);
1144     if (i > 0)
1145       *p++ = ',';
1146     snprintf(p, sizeof(out) - (p - out),
1147       po->operand[i].type == OPT_REGMEM ? " [%s]" : " %s",
1148       po->operand[i].name);
1149   }
1150
1151   return out;
1152 }
1153
1154 static const char *lmod_type_u(struct parsed_op *po,
1155   enum opr_lenmod lmod)
1156 {
1157   switch (lmod) {
1158   case OPLM_DWORD:
1159     return "u32";
1160   case OPLM_WORD:
1161     return "u16";
1162   case OPLM_BYTE:
1163     return "u8";
1164   default:
1165     ferr(po, "invalid lmod: %d\n", lmod);
1166     return "(_invalid_)";
1167   }
1168 }
1169
1170 static const char *lmod_cast_u(struct parsed_op *po,
1171   enum opr_lenmod lmod)
1172 {
1173   switch (lmod) {
1174   case OPLM_DWORD:
1175     return "";
1176   case OPLM_WORD:
1177     return "(u16)";
1178   case OPLM_BYTE:
1179     return "(u8)";
1180   default:
1181     ferr(po, "invalid lmod: %d\n", lmod);
1182     return "(_invalid_)";
1183   }
1184 }
1185
1186 static const char *lmod_cast_u_ptr(struct parsed_op *po,
1187   enum opr_lenmod lmod)
1188 {
1189   switch (lmod) {
1190   case OPLM_DWORD:
1191     return "*(u32 *)";
1192   case OPLM_WORD:
1193     return "*(u16 *)";
1194   case OPLM_BYTE:
1195     return "*(u8 *)";
1196   default:
1197     ferr(po, "invalid lmod: %d\n", lmod);
1198     return "(_invalid_)";
1199   }
1200 }
1201
1202 static const char *lmod_cast_s(struct parsed_op *po,
1203   enum opr_lenmod lmod)
1204 {
1205   switch (lmod) {
1206   case OPLM_DWORD:
1207     return "(s32)";
1208   case OPLM_WORD:
1209     return "(s16)";
1210   case OPLM_BYTE:
1211     return "(s8)";
1212   default:
1213     ferr(po, "%s: invalid lmod: %d\n", __func__, lmod);
1214     return "(_invalid_)";
1215   }
1216 }
1217
1218 static const char *lmod_cast(struct parsed_op *po,
1219   enum opr_lenmod lmod, int is_signed)
1220 {
1221   return is_signed ?
1222     lmod_cast_s(po, lmod) :
1223     lmod_cast_u(po, lmod);
1224 }
1225
1226 static int lmod_bytes(struct parsed_op *po, enum opr_lenmod lmod)
1227 {
1228   switch (lmod) {
1229   case OPLM_DWORD:
1230     return 4;
1231   case OPLM_WORD:
1232     return 2;
1233   case OPLM_BYTE:
1234     return 1;
1235   default:
1236     ferr(po, "%s: invalid lmod: %d\n", __func__, lmod);
1237     return 0;
1238   }
1239 }
1240
1241 static const char *opr_name(struct parsed_op *po, int opr_num)
1242 {
1243   if (opr_num >= po->operand_cnt)
1244     ferr(po, "opr OOR: %d/%d\n", opr_num, po->operand_cnt);
1245   return po->operand[opr_num].name;
1246 }
1247
1248 static unsigned int opr_const(struct parsed_op *po, int opr_num)
1249 {
1250   if (opr_num >= po->operand_cnt)
1251     ferr(po, "opr OOR: %d/%d\n", opr_num, po->operand_cnt);
1252   if (po->operand[opr_num].type != OPT_CONST)
1253     ferr(po, "opr %d: const expected\n", opr_num);
1254   return po->operand[opr_num].val;
1255 }
1256
1257 static const char *opr_reg_p(struct parsed_op *po, struct parsed_opr *popr)
1258 {
1259   if ((unsigned int)popr->reg >= MAX_REGS)
1260     ferr(po, "invalid reg: %d\n", popr->reg);
1261   return regs_r32[popr->reg];
1262 }
1263
1264 // cast1 is the "final" cast
1265 static const char *simplify_cast(const char *cast1, const char *cast2)
1266 {
1267   static char buf[256];
1268
1269   if (cast1[0] == 0)
1270     return cast2;
1271   if (cast2[0] == 0)
1272     return cast1;
1273   if (IS(cast1, cast2))
1274     return cast1;
1275   if (IS(cast1, "(s8)") && IS(cast2, "(u8)"))
1276     return cast1;
1277   if (IS(cast1, "(s16)") && IS(cast2, "(u16)"))
1278     return cast1;
1279   if (IS(cast1, "(u8)") && IS_START(cast2, "*(u8 *)"))
1280     return cast2;
1281   if (IS(cast1, "(u16)") && IS_START(cast2, "*(u16 *)"))
1282     return cast2;
1283   if (strchr(cast1, '*') && IS_START(cast2, "(u32)"))
1284     return cast1;
1285
1286   snprintf(buf, sizeof(buf), "%s%s", cast1, cast2);
1287   return buf;
1288 }
1289
1290 static const char *simplify_cast_num(const char *cast, unsigned int val)
1291 {
1292   if (IS(cast, "(u8)") && val < 0x100)
1293     return "";
1294   if (IS(cast, "(s8)") && val < 0x80)
1295     return "";
1296   if (IS(cast, "(u16)") && val < 0x10000)
1297     return "";
1298   if (IS(cast, "(s16)") && val < 0x8000)
1299     return "";
1300   if (IS(cast, "(s32)") && val < 0x80000000)
1301     return "";
1302
1303   return cast;
1304 }
1305
1306 static struct parsed_equ *equ_find(struct parsed_op *po, const char *name,
1307   int *extra_offs)
1308 {
1309   const char *p;
1310   char *endp;
1311   int namelen;
1312   int i;
1313
1314   *extra_offs = 0;
1315   namelen = strlen(name);
1316
1317   p = strchr(name, '+');
1318   if (p != NULL) {
1319     namelen = p - name;
1320     if (namelen <= 0)
1321       ferr(po, "equ parse failed for '%s'\n", name);
1322
1323     if (IS_START(p, "0x"))
1324       p += 2;
1325     *extra_offs = strtol(p, &endp, 16);
1326     if (*endp != 0)
1327       ferr(po, "equ parse failed for '%s'\n", name);
1328   }
1329
1330   for (i = 0; i < g_eqcnt; i++)
1331     if (strncmp(g_eqs[i].name, name, namelen) == 0
1332      && g_eqs[i].name[namelen] == 0)
1333       break;
1334   if (i >= g_eqcnt) {
1335     if (po != NULL)
1336       ferr(po, "unresolved equ name: '%s'\n", name);
1337     return NULL;
1338   }
1339
1340   return &g_eqs[i];
1341 }
1342
1343 static int is_stack_access(struct parsed_op *po,
1344   const struct parsed_opr *popr)
1345 {
1346   return (parse_stack_el(popr->name, NULL, 0)
1347     || (g_bp_frame && !(po->flags & OPF_EBP_S)
1348         && IS_START(popr->name, "ebp")));
1349 }
1350
1351 static void parse_stack_access(struct parsed_op *po,
1352   const char *name, char *ofs_reg, int *offset_out,
1353   int *stack_ra_out, const char **bp_arg_out, int is_lea)
1354 {
1355   const char *bp_arg = "";
1356   const char *p = NULL;
1357   struct parsed_equ *eq;
1358   char *endp = NULL;
1359   int stack_ra = 0;
1360   int offset = 0;
1361
1362   ofs_reg[0] = 0;
1363
1364   if (IS_START(name, "ebp-")
1365    || (IS_START(name, "ebp+") && '0' <= name[4] && name[4] <= '9'))
1366   {
1367     p = name + 4;
1368     if (IS_START(p, "0x"))
1369       p += 2;
1370     offset = strtoul(p, &endp, 16);
1371     if (name[3] == '-')
1372       offset = -offset;
1373     if (*endp != 0)
1374       ferr(po, "ebp- parse of '%s' failed\n", name);
1375   }
1376   else {
1377     bp_arg = parse_stack_el(name, ofs_reg, 0);
1378     snprintf(g_comment, sizeof(g_comment), "%s", bp_arg);
1379     eq = equ_find(po, bp_arg, &offset);
1380     if (eq == NULL)
1381       ferr(po, "detected but missing eq\n");
1382     offset += eq->offset;
1383   }
1384
1385   if (!strncmp(name, "ebp", 3))
1386     stack_ra = 4;
1387
1388   // yes it sometimes LEAs ra for compares..
1389   if (!is_lea && ofs_reg[0] == 0
1390     && stack_ra <= offset && offset < stack_ra + 4)
1391   {
1392     ferr(po, "reference to ra? %d %d\n", offset, stack_ra);
1393   }
1394
1395   *offset_out = offset;
1396   *stack_ra_out = stack_ra;
1397   if (bp_arg_out)
1398     *bp_arg_out = bp_arg;
1399 }
1400
1401 static int stack_frame_access(struct parsed_op *po,
1402   struct parsed_opr *popr, char *buf, size_t buf_size,
1403   const char *name, const char *cast, int is_src, int is_lea)
1404 {
1405   enum opr_lenmod tmp_lmod = OPLM_UNSPEC;
1406   const char *prefix = "";
1407   const char *bp_arg = NULL;
1408   char ofs_reg[16] = { 0, };
1409   int i, arg_i, arg_s;
1410   int unaligned = 0;
1411   int stack_ra = 0;
1412   int offset = 0;
1413   int retval = -1;
1414   int sf_ofs;
1415   int lim;
1416
1417   if (po->flags & OPF_EBP_S)
1418     ferr(po, "stack_frame_access while ebp is scratch\n");
1419
1420   parse_stack_access(po, name, ofs_reg, &offset,
1421     &stack_ra, &bp_arg, is_lea);
1422
1423   if (offset > stack_ra)
1424   {
1425     arg_i = (offset - stack_ra - 4) / 4;
1426     if (arg_i < 0 || arg_i >= g_func_pp->argc_stack)
1427     {
1428       if (g_func_pp->is_vararg
1429           && arg_i == g_func_pp->argc_stack && is_lea)
1430       {
1431         // should be va_list
1432         if (cast[0] == 0)
1433           cast = "(u32)";
1434         snprintf(buf, buf_size, "%sap", cast);
1435         return -1;
1436       }
1437       ferr(po, "offset %d (%s,%d) doesn't map to any arg\n",
1438         offset, bp_arg, arg_i);
1439     }
1440     if (ofs_reg[0] != 0)
1441       ferr(po, "offset reg on arg access?\n");
1442
1443     for (i = arg_s = 0; i < g_func_pp->argc; i++) {
1444       if (g_func_pp->arg[i].reg != NULL)
1445         continue;
1446       if (arg_s == arg_i)
1447         break;
1448       arg_s++;
1449     }
1450     if (i == g_func_pp->argc)
1451       ferr(po, "arg %d not in prototype?\n", arg_i);
1452
1453     popr->is_ptr = g_func_pp->arg[i].type.is_ptr;
1454     retval = i;
1455
1456     switch (popr->lmod)
1457     {
1458     case OPLM_BYTE:
1459       if (is_lea)
1460         ferr(po, "lea/byte to arg?\n");
1461       if (is_src && (offset & 3) == 0)
1462         snprintf(buf, buf_size, "%sa%d",
1463           simplify_cast(cast, "(u8)"), i + 1);
1464       else
1465         snprintf(buf, buf_size, "%sBYTE%d(a%d)",
1466           cast, offset & 3, i + 1);
1467       break;
1468
1469     case OPLM_WORD:
1470       if (is_lea)
1471         ferr(po, "lea/word to arg?\n");
1472       if (offset & 1) {
1473         unaligned = 1;
1474         if (!is_src) {
1475           if (offset & 2)
1476             ferr(po, "problematic arg store\n");
1477           snprintf(buf, buf_size, "%s((char *)&a%d + 1)",
1478             simplify_cast(cast, "*(u16 *)"), i + 1);
1479         }
1480         else
1481           ferr(po, "unaligned arg word load\n");
1482       }
1483       else if (is_src && (offset & 2) == 0)
1484         snprintf(buf, buf_size, "%sa%d",
1485           simplify_cast(cast, "(u16)"), i + 1);
1486       else
1487         snprintf(buf, buf_size, "%s%sWORD(a%d)",
1488           cast, (offset & 2) ? "HI" : "LO", i + 1);
1489       break;
1490
1491     case OPLM_DWORD:
1492       if (cast[0])
1493         prefix = cast;
1494       else if (is_src)
1495         prefix = "(u32)";
1496
1497       if (offset & 3) {
1498         unaligned = 1;
1499         if (is_lea)
1500           snprintf(buf, buf_size, "(u32)&a%d + %d",
1501             i + 1, offset & 3);
1502         else if (!is_src)
1503           ferr(po, "unaligned arg store\n");
1504         else {
1505           // mov edx, [ebp+arg_4+2]; movsx ecx, dx
1506           snprintf(buf, buf_size, "%s(a%d >> %d)",
1507             prefix, i + 1, (offset & 3) * 8);
1508         }
1509       }
1510       else {
1511         snprintf(buf, buf_size, "%s%sa%d",
1512           prefix, is_lea ? "&" : "", i + 1);
1513       }
1514       break;
1515
1516     default:
1517       ferr(po, "bp_arg bad lmod: %d\n", popr->lmod);
1518     }
1519
1520     if (unaligned)
1521       snprintf(g_comment, sizeof(g_comment), "%s unaligned", bp_arg);
1522
1523     // common problem
1524     guess_lmod_from_c_type(&tmp_lmod, &g_func_pp->arg[i].type);
1525     if (tmp_lmod != OPLM_DWORD
1526       && (unaligned || (!is_src && lmod_bytes(po, tmp_lmod)
1527                          < lmod_bytes(po, popr->lmod) + (offset & 3))))
1528     {
1529       ferr(po, "bp_arg arg%d/w offset %d and type '%s' is too small\n",
1530         i + 1, offset, g_func_pp->arg[i].type.name);
1531     }
1532     // can't check this because msvc likes to reuse
1533     // arg space for scratch..
1534     //if (popr->is_ptr && popr->lmod != OPLM_DWORD)
1535     //  ferr(po, "bp_arg arg%d: non-dword ptr access\n", i + 1);
1536   }
1537   else
1538   {
1539     if (g_stack_fsz == 0)
1540       ferr(po, "stack var access without stackframe\n");
1541     g_stack_frame_used = 1;
1542
1543     sf_ofs = g_stack_fsz + offset;
1544     lim = (ofs_reg[0] != 0) ? -4 : 0;
1545     if (offset > 0 || sf_ofs < lim)
1546       ferr(po, "bp_stack offset %d/%d\n", offset, g_stack_fsz);
1547
1548     if (is_lea)
1549       prefix = "(u32)&";
1550     else
1551       prefix = cast;
1552
1553     switch (popr->lmod)
1554     {
1555     case OPLM_BYTE:
1556       snprintf(buf, buf_size, "%ssf.b[%d%s%s]",
1557         prefix, sf_ofs, ofs_reg[0] ? "+" : "", ofs_reg);
1558       break;
1559
1560     case OPLM_WORD:
1561       if ((sf_ofs & 1) || ofs_reg[0] != 0) {
1562         // known unaligned or possibly unaligned
1563         strcat(g_comment, " unaligned");
1564         if (prefix[0] == 0)
1565           prefix = "*(u16 *)&";
1566         snprintf(buf, buf_size, "%ssf.b[%d%s%s]",
1567           prefix, sf_ofs, ofs_reg[0] ? "+" : "", ofs_reg);
1568         break;
1569       }
1570       snprintf(buf, buf_size, "%ssf.w[%d]", prefix, sf_ofs / 2);
1571       break;
1572
1573     case OPLM_DWORD:
1574       if ((sf_ofs & 3) || ofs_reg[0] != 0) {
1575         // known unaligned or possibly unaligned
1576         strcat(g_comment, " unaligned");
1577         if (prefix[0] == 0)
1578           prefix = "*(u32 *)&";
1579         snprintf(buf, buf_size, "%ssf.b[%d%s%s]",
1580           prefix, sf_ofs, ofs_reg[0] ? "+" : "", ofs_reg);
1581         break;
1582       }
1583       snprintf(buf, buf_size, "%ssf.d[%d]", prefix, sf_ofs / 4);
1584       break;
1585
1586     default:
1587       ferr(po, "bp_stack bad lmod: %d\n", popr->lmod);
1588     }
1589   }
1590
1591   return retval;
1592 }
1593
1594 static void check_func_pp(struct parsed_op *po,
1595   const struct parsed_proto *pp, const char *pfx)
1596 {
1597   enum opr_lenmod tmp_lmod;
1598   char buf[256];
1599   int ret, i;
1600
1601   if (pp->argc_reg != 0) {
1602     if (/*!g_allow_regfunc &&*/ !pp->is_fastcall) {
1603       pp_print(buf, sizeof(buf), pp);
1604       ferr(po, "%s: unexpected reg arg in icall: %s\n", pfx, buf);
1605     }
1606     if (pp->argc_stack > 0 && pp->argc_reg != 2)
1607       ferr(po, "%s: %d reg arg(s) with %d stack arg(s)\n",
1608         pfx, pp->argc_reg, pp->argc_stack);
1609   }
1610
1611   // fptrs must use 32bit args, callsite might have no information and
1612   // lack a cast to smaller types, which results in incorrectly masked
1613   // args passed (callee may assume masked args, it does on ARM)
1614   if (!pp->is_oslib) {
1615     for (i = 0; i < pp->argc; i++) {
1616       ret = guess_lmod_from_c_type(&tmp_lmod, &pp->arg[i].type);
1617       if (ret && tmp_lmod != OPLM_DWORD)
1618         ferr(po, "reference to %s with arg%d '%s'\n", pp->name,
1619           i + 1, pp->arg[i].type.name);
1620     }
1621   }
1622 }
1623
1624 static const char *check_label_read_ref(struct parsed_op *po,
1625   const char *name)
1626 {
1627   const struct parsed_proto *pp;
1628
1629   pp = proto_parse(g_fhdr, name, 0);
1630   if (pp == NULL)
1631     ferr(po, "proto_parse failed for ref '%s'\n", name);
1632
1633   if (pp->is_func)
1634     check_func_pp(po, pp, "ref");
1635
1636   return pp->name;
1637 }
1638
1639 static char *out_src_opr(char *buf, size_t buf_size,
1640   struct parsed_op *po, struct parsed_opr *popr, const char *cast,
1641   int is_lea)
1642 {
1643   char tmp1[256], tmp2[256];
1644   char expr[256];
1645   const char *name;
1646   char *p;
1647   int ret;
1648
1649   if (cast == NULL)
1650     cast = "";
1651
1652   switch (popr->type) {
1653   case OPT_REG:
1654     if (is_lea)
1655       ferr(po, "lea from reg?\n");
1656
1657     switch (popr->lmod) {
1658     case OPLM_DWORD:
1659       snprintf(buf, buf_size, "%s%s", cast, opr_reg_p(po, popr));
1660       break;
1661     case OPLM_WORD:
1662       snprintf(buf, buf_size, "%s%s",
1663         simplify_cast(cast, "(u16)"), opr_reg_p(po, popr));
1664       break;
1665     case OPLM_BYTE:
1666       if (popr->name[1] == 'h') // XXX..
1667         snprintf(buf, buf_size, "%s(%s >> 8)",
1668           simplify_cast(cast, "(u8)"), opr_reg_p(po, popr));
1669       else
1670         snprintf(buf, buf_size, "%s%s",
1671           simplify_cast(cast, "(u8)"), opr_reg_p(po, popr));
1672       break;
1673     default:
1674       ferr(po, "invalid src lmod: %d\n", popr->lmod);
1675     }
1676     break;
1677
1678   case OPT_REGMEM:
1679     if (is_stack_access(po, popr)) {
1680       stack_frame_access(po, popr, buf, buf_size,
1681         popr->name, cast, 1, is_lea);
1682       break;
1683     }
1684
1685     strcpy(expr, popr->name);
1686     if (strchr(expr, '[')) {
1687       // special case: '[' can only be left for label[reg] form
1688       ret = sscanf(expr, "%[^[][%[^]]]", tmp1, tmp2);
1689       if (ret != 2)
1690         ferr(po, "parse failure for '%s'\n", expr);
1691       if (tmp1[0] == '(') {
1692         // (off_4FFF50+3)[eax]
1693         p = strchr(tmp1 + 1, ')');
1694         if (p == NULL || p[1] != 0)
1695           ferr(po, "parse failure (2) for '%s'\n", expr);
1696         *p = 0;
1697         memmove(tmp1, tmp1 + 1, strlen(tmp1));
1698       }
1699       snprintf(expr, sizeof(expr), "(u32)&%s + %s", tmp1, tmp2);
1700     }
1701
1702     // XXX: do we need more parsing?
1703     if (is_lea) {
1704       snprintf(buf, buf_size, "%s", expr);
1705       break;
1706     }
1707
1708     snprintf(buf, buf_size, "%s(%s)",
1709       simplify_cast(cast, lmod_cast_u_ptr(po, popr->lmod)), expr);
1710     break;
1711
1712   case OPT_LABEL:
1713     name = check_label_read_ref(po, popr->name);
1714     if (cast[0] == 0 && popr->is_ptr)
1715       cast = "(u32)";
1716
1717     if (is_lea)
1718       snprintf(buf, buf_size, "(u32)&%s", name);
1719     else if (popr->size_lt)
1720       snprintf(buf, buf_size, "%s%s%s%s", cast,
1721         lmod_cast_u_ptr(po, popr->lmod),
1722         popr->is_array ? "" : "&", name);
1723     else
1724       snprintf(buf, buf_size, "%s%s%s", cast, name,
1725         popr->is_array ? "[0]" : "");
1726     break;
1727
1728   case OPT_OFFSET:
1729     name = check_label_read_ref(po, popr->name);
1730     if (cast[0] == 0)
1731       cast = "(u32)";
1732     if (is_lea)
1733       ferr(po, "lea an offset?\n");
1734     snprintf(buf, buf_size, "%s&%s", cast, name);
1735     break;
1736
1737   case OPT_CONST:
1738     if (is_lea)
1739       ferr(po, "lea from const?\n");
1740
1741     printf_number(tmp1, sizeof(tmp1), popr->val);
1742     if (popr->val == 0 && strchr(cast, '*'))
1743       snprintf(buf, buf_size, "NULL");
1744     else
1745       snprintf(buf, buf_size, "%s%s",
1746         simplify_cast_num(cast, popr->val), tmp1);
1747     break;
1748
1749   default:
1750     ferr(po, "invalid src type: %d\n", popr->type);
1751   }
1752
1753   return buf;
1754 }
1755
1756 // note: may set is_ptr (we find that out late for ebp frame..)
1757 static char *out_dst_opr(char *buf, size_t buf_size,
1758         struct parsed_op *po, struct parsed_opr *popr)
1759 {
1760   switch (popr->type) {
1761   case OPT_REG:
1762     switch (popr->lmod) {
1763     case OPLM_DWORD:
1764       snprintf(buf, buf_size, "%s", opr_reg_p(po, popr));
1765       break;
1766     case OPLM_WORD:
1767       // ugh..
1768       snprintf(buf, buf_size, "LOWORD(%s)", opr_reg_p(po, popr));
1769       break;
1770     case OPLM_BYTE:
1771       // ugh..
1772       if (popr->name[1] == 'h') // XXX..
1773         snprintf(buf, buf_size, "BYTE1(%s)", opr_reg_p(po, popr));
1774       else
1775         snprintf(buf, buf_size, "LOBYTE(%s)", opr_reg_p(po, popr));
1776       break;
1777     default:
1778       ferr(po, "invalid dst lmod: %d\n", popr->lmod);
1779     }
1780     break;
1781
1782   case OPT_REGMEM:
1783     if (is_stack_access(po, popr)) {
1784       stack_frame_access(po, popr, buf, buf_size,
1785         popr->name, "", 0, 0);
1786       break;
1787     }
1788
1789     return out_src_opr(buf, buf_size, po, popr, NULL, 0);
1790
1791   case OPT_LABEL:
1792     if (popr->size_mismatch)
1793       snprintf(buf, buf_size, "%s%s%s",
1794         lmod_cast_u_ptr(po, popr->lmod),
1795         popr->is_array ? "" : "&", popr->name);
1796     else
1797       snprintf(buf, buf_size, "%s%s", popr->name,
1798         popr->is_array ? "[0]" : "");
1799     break;
1800
1801   default:
1802     ferr(po, "invalid dst type: %d\n", popr->type);
1803   }
1804
1805   return buf;
1806 }
1807
1808 static char *out_src_opr_u32(char *buf, size_t buf_size,
1809         struct parsed_op *po, struct parsed_opr *popr)
1810 {
1811   return out_src_opr(buf, buf_size, po, popr, NULL, 0);
1812 }
1813
1814 static void out_test_for_cc(char *buf, size_t buf_size,
1815   struct parsed_op *po, enum parsed_flag_op pfo, int is_inv,
1816   enum opr_lenmod lmod, const char *expr)
1817 {
1818   const char *cast, *scast;
1819
1820   cast = lmod_cast_u(po, lmod);
1821   scast = lmod_cast_s(po, lmod);
1822
1823   switch (pfo) {
1824   case PFO_Z:
1825   case PFO_BE: // CF=1||ZF=1; CF=0
1826     snprintf(buf, buf_size, "(%s%s %s 0)",
1827       cast, expr, is_inv ? "!=" : "==");
1828     break;
1829
1830   case PFO_S:
1831   case PFO_L: // SF!=OF; OF=0
1832     snprintf(buf, buf_size, "(%s%s %s 0)",
1833       scast, expr, is_inv ? ">=" : "<");
1834     break;
1835
1836   case PFO_LE: // ZF=1||SF!=OF; OF=0
1837     snprintf(buf, buf_size, "(%s%s %s 0)",
1838       scast, expr, is_inv ? ">" : "<=");
1839     break;
1840
1841   default:
1842     ferr(po, "%s: unhandled parsed_flag_op: %d\n", __func__, pfo);
1843   }
1844 }
1845
1846 static void out_cmp_for_cc(char *buf, size_t buf_size,
1847   struct parsed_op *po, enum parsed_flag_op pfo, int is_inv)
1848 {
1849   const char *cast, *scast, *cast_use;
1850   char buf1[256], buf2[256];
1851   enum opr_lenmod lmod;
1852
1853   if (po->operand[0].lmod != po->operand[1].lmod)
1854     ferr(po, "%s: lmod mismatch: %d %d\n", __func__,
1855       po->operand[0].lmod, po->operand[1].lmod);
1856   lmod = po->operand[0].lmod;
1857
1858   cast = lmod_cast_u(po, lmod);
1859   scast = lmod_cast_s(po, lmod);
1860
1861   switch (pfo) {
1862   case PFO_C:
1863   case PFO_Z:
1864   case PFO_BE: // !a
1865     cast_use = cast;
1866     break;
1867
1868   case PFO_S:
1869   case PFO_L: // !ge
1870   case PFO_LE:
1871     cast_use = scast;
1872     break;
1873
1874   default:
1875     ferr(po, "%s: unhandled parsed_flag_op: %d\n", __func__, pfo);
1876   }
1877
1878   out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], cast_use, 0);
1879   out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], cast_use, 0);
1880
1881   switch (pfo) {
1882   case PFO_C:
1883     // note: must be unsigned compare
1884     snprintf(buf, buf_size, "(%s %s %s)",
1885       buf1, is_inv ? ">=" : "<", buf2);
1886     break;
1887
1888   case PFO_Z:
1889     snprintf(buf, buf_size, "(%s %s %s)",
1890       buf1, is_inv ? "!=" : "==", buf2);
1891     break;
1892
1893   case PFO_BE: // !a
1894     // note: must be unsigned compare
1895     snprintf(buf, buf_size, "(%s %s %s)",
1896       buf1, is_inv ? ">" : "<=", buf2);
1897
1898     // annoying case
1899     if (is_inv && lmod == OPLM_BYTE
1900       && po->operand[1].type == OPT_CONST
1901       && po->operand[1].val == 0xff)
1902     {
1903       snprintf(g_comment, sizeof(g_comment), "if %s", buf);
1904       snprintf(buf, buf_size, "(0)");
1905     }
1906     break;
1907
1908   // note: must be signed compare
1909   case PFO_S:
1910     snprintf(buf, buf_size, "(%s(%s - %s) %s 0)",
1911       scast, buf1, buf2, is_inv ? ">=" : "<");
1912     break;
1913
1914   case PFO_L: // !ge
1915     snprintf(buf, buf_size, "(%s %s %s)",
1916       buf1, is_inv ? ">=" : "<", buf2);
1917     break;
1918
1919   case PFO_LE:
1920     snprintf(buf, buf_size, "(%s %s %s)",
1921       buf1, is_inv ? ">" : "<=", buf2);
1922     break;
1923
1924   default:
1925     break;
1926   }
1927 }
1928
1929 static void out_cmp_test(char *buf, size_t buf_size,
1930   struct parsed_op *po, enum parsed_flag_op pfo, int is_inv)
1931 {
1932   char buf1[256], buf2[256], buf3[256];
1933
1934   if (po->op == OP_TEST) {
1935     if (IS(opr_name(po, 0), opr_name(po, 1))) {
1936       out_src_opr_u32(buf3, sizeof(buf3), po, &po->operand[0]);
1937     }
1938     else {
1939       out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
1940       out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
1941       snprintf(buf3, sizeof(buf3), "(%s & %s)", buf1, buf2);
1942     }
1943     out_test_for_cc(buf, buf_size, po, pfo, is_inv,
1944       po->operand[0].lmod, buf3);
1945   }
1946   else if (po->op == OP_CMP) {
1947     out_cmp_for_cc(buf, buf_size, po, pfo, is_inv);
1948   }
1949   else
1950     ferr(po, "%s: unhandled op: %d\n", __func__, po->op);
1951 }
1952
1953 static void propagate_lmod(struct parsed_op *po, struct parsed_opr *popr1,
1954         struct parsed_opr *popr2)
1955 {
1956   if (popr1->lmod == OPLM_UNSPEC && popr2->lmod == OPLM_UNSPEC)
1957     ferr(po, "missing lmod for both operands\n");
1958
1959   if (popr1->lmod == OPLM_UNSPEC)
1960     popr1->lmod = popr2->lmod;
1961   else if (popr2->lmod == OPLM_UNSPEC)
1962     popr2->lmod = popr1->lmod;
1963   else if (popr1->lmod != popr2->lmod) {
1964     if (popr1->type_from_var) {
1965       popr1->size_mismatch = 1;
1966       if (popr1->lmod < popr2->lmod)
1967         popr1->size_lt = 1;
1968       popr1->lmod = popr2->lmod;
1969     }
1970     else if (popr2->type_from_var) {
1971       popr2->size_mismatch = 1;
1972       if (popr2->lmod < popr1->lmod)
1973         popr2->size_lt = 1;
1974       popr2->lmod = popr1->lmod;
1975     }
1976     else
1977       ferr(po, "conflicting lmods: %d vs %d\n",
1978         popr1->lmod, popr2->lmod);
1979   }
1980 }
1981
1982 static const char *op_to_c(struct parsed_op *po)
1983 {
1984   switch (po->op)
1985   {
1986     case OP_ADD:
1987     case OP_ADC:
1988       return "+";
1989     case OP_SUB:
1990     case OP_SBB:
1991       return "-";
1992     case OP_AND:
1993       return "&";
1994     case OP_OR:
1995       return "|";
1996     case OP_XOR:
1997       return "^";
1998     case OP_SHL:
1999       return "<<";
2000     case OP_SHR:
2001       return ">>";
2002     case OP_MUL:
2003     case OP_IMUL:
2004       return "*";
2005     default:
2006       ferr(po, "op_to_c was supplied with %d\n", po->op);
2007   }
2008 }
2009
2010 static void op_set_clear_flag(struct parsed_op *po,
2011   enum op_flags flag_set, enum op_flags flag_clear)
2012 {
2013   po->flags |= flag_set;
2014   po->flags &= ~flag_clear;
2015 }
2016
2017 // last op in stream - unconditional branch or ret
2018 #define LAST_OP(_i) ((ops[_i].flags & OPF_TAIL) \
2019   || ((ops[_i].flags & (OPF_JMP|OPF_CJMP|OPF_RMD)) == OPF_JMP \
2020       && ops[_i].op != OP_CALL))
2021
2022 static int scan_for_pop(int i, int opcnt, const char *reg,
2023   int magic, int depth, int *maxdepth, int do_flags)
2024 {
2025   const struct parsed_proto *pp;
2026   struct parsed_op *po;
2027   int ret = 0;
2028   int j;
2029
2030   for (; i < opcnt; i++) {
2031     po = &ops[i];
2032     if (po->cc_scratch == magic)
2033       break; // already checked
2034     po->cc_scratch = magic;
2035
2036     if (po->flags & OPF_TAIL) {
2037       if (po->op == OP_CALL) {
2038         pp = proto_parse(g_fhdr, po->operand[0].name, 0);
2039         if (pp != NULL && pp->is_noreturn)
2040           // no stack cleanup for noreturn
2041           return ret;
2042       }
2043       return -1; // deadend
2044     }
2045
2046     if ((po->flags & OPF_RMD)
2047         || (po->op == OP_PUSH && po->p_argnum != 0)) // arg push
2048       continue;
2049
2050     if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
2051       if (po->btj != NULL) {
2052         // jumptable
2053         for (j = 0; j < po->btj->count; j++) {
2054           ret |= scan_for_pop(po->btj->d[j].bt_i, opcnt, reg, magic,
2055                    depth, maxdepth, do_flags);
2056           if (ret < 0)
2057             return ret; // dead end
2058         }
2059         return ret;
2060       }
2061
2062       if (po->bt_i < 0) {
2063         ferr(po, "dead branch\n");
2064         return -1;
2065       }
2066
2067       if (po->flags & OPF_CJMP) {
2068         ret |= scan_for_pop(po->bt_i, opcnt, reg, magic,
2069                  depth, maxdepth, do_flags);
2070         if (ret < 0)
2071           return ret; // dead end
2072       }
2073       else {
2074         i = po->bt_i - 1;
2075       }
2076       continue;
2077     }
2078
2079     if ((po->op == OP_POP || po->op == OP_PUSH)
2080         && po->operand[0].type == OPT_REG
2081         && IS(po->operand[0].name, reg))
2082     {
2083       if (po->op == OP_PUSH && !(po->flags & OPF_FARG)) {
2084         depth++;
2085         if (depth > *maxdepth)
2086           *maxdepth = depth;
2087         if (do_flags)
2088           op_set_clear_flag(po, OPF_RSAVE, OPF_RMD);
2089       }
2090       else if (po->op == OP_POP) {
2091         if (depth == 0) {
2092           if (do_flags)
2093             op_set_clear_flag(po, OPF_RMD, OPF_RSAVE);
2094           return 1;
2095         }
2096         else {
2097           depth--;
2098           if (depth < 0) // should not happen
2099             ferr(po, "fail with depth\n");
2100           if (do_flags)
2101             op_set_clear_flag(po, OPF_RSAVE, OPF_RMD);
2102         }
2103       }
2104     }
2105   }
2106
2107   return ret;
2108 }
2109
2110 // scan for pop starting from 'ret' op (all paths)
2111 static int scan_for_pop_ret(int i, int opcnt, const char *reg,
2112   int flag_set)
2113 {
2114   int found = 0;
2115   int j;
2116
2117   for (; i < opcnt; i++) {
2118     if (!(ops[i].flags & OPF_TAIL))
2119       continue;
2120
2121     for (j = i - 1; j >= 0; j--) {
2122       if (ops[j].flags & OPF_RMD)
2123         continue;
2124       if (ops[j].flags & OPF_JMP)
2125         return -1;
2126
2127       if (ops[j].op == OP_POP && ops[j].operand[0].type == OPT_REG
2128           && IS(ops[j].operand[0].name, reg))
2129       {
2130         found = 1;
2131         ops[j].flags |= flag_set;
2132         break;
2133       }
2134
2135       if (g_labels[j][0] != 0)
2136         return -1;
2137     }
2138   }
2139
2140   return found ? 0 : -1;
2141 }
2142
2143 static void scan_propagate_df(int i, int opcnt)
2144 {
2145   struct parsed_op *po = &ops[i];
2146   int j;
2147
2148   for (; i < opcnt; i++) {
2149     po = &ops[i];
2150     if (po->flags & OPF_DF)
2151       return; // already resolved
2152     po->flags |= OPF_DF;
2153
2154     if (po->op == OP_CALL)
2155       ferr(po, "call with DF set?\n");
2156
2157     if (po->flags & OPF_JMP) {
2158       if (po->btj != NULL) {
2159         // jumptable
2160         for (j = 0; j < po->btj->count; j++)
2161           scan_propagate_df(po->btj->d[j].bt_i, opcnt);
2162         return;
2163       }
2164
2165       if (po->bt_i < 0) {
2166         ferr(po, "dead branch\n");
2167         return;
2168       }
2169
2170       if (po->flags & OPF_CJMP)
2171         scan_propagate_df(po->bt_i, opcnt);
2172       else
2173         i = po->bt_i - 1;
2174       continue;
2175     }
2176
2177     if (po->flags & OPF_TAIL)
2178       break;
2179
2180     if (po->op == OP_CLD) {
2181       po->flags |= OPF_RMD;
2182       return;
2183     }
2184   }
2185
2186   ferr(po, "missing DF clear?\n");
2187 }
2188
2189 // is operand 'opr' modified by parsed_op 'po'?
2190 static int is_opr_modified(const struct parsed_opr *opr,
2191   const struct parsed_op *po)
2192 {
2193   int mask;
2194
2195   if ((po->flags & OPF_RMD) || !(po->flags & OPF_DATA))
2196     return 0;
2197
2198   if (opr->type == OPT_REG) {
2199     if (po->op == OP_CALL) {
2200       mask = (1 << xAX) | (1 << xCX) | (1 << xDX);
2201       if ((1 << opr->reg) & mask)
2202         return 1;
2203       else
2204         return 0;
2205     }
2206
2207     if (po->operand[0].type == OPT_REG) {
2208       if (po->regmask_dst & (1 << opr->reg))
2209         return 1;
2210       else
2211         return 0;
2212     }
2213   }
2214
2215   return IS(po->operand[0].name, opr->name);
2216 }
2217
2218 // is any operand of parsed_op 'po_test' modified by parsed_op 'po'?
2219 static int is_any_opr_modified(const struct parsed_op *po_test,
2220   const struct parsed_op *po, int c_mode)
2221 {
2222   int mask;
2223   int i;
2224
2225   if ((po->flags & OPF_RMD) || !(po->flags & OPF_DATA))
2226     return 0;
2227
2228   if (po_test->operand_cnt == 1 && po_test->operand[0].type == OPT_CONST)
2229     return 0;
2230
2231   if ((po_test->regmask_src | po_test->regmask_dst) & po->regmask_dst)
2232     return 1;
2233
2234   // in reality, it can wreck any register, but in decompiled C
2235   // version it can only overwrite eax or edx:eax
2236   mask = (1 << xAX) | (1 << xDX);
2237   if (!c_mode)
2238     mask |= 1 << xCX;
2239
2240   if (po->op == OP_CALL
2241    && ((po_test->regmask_src | po_test->regmask_dst) & mask))
2242     return 1;
2243
2244   for (i = 0; i < po_test->operand_cnt; i++)
2245     if (IS(po_test->operand[i].name, po->operand[0].name))
2246       return 1;
2247
2248   return 0;
2249 }
2250
2251 // scan for any po_test operand modification in range given
2252 static int scan_for_mod(struct parsed_op *po_test, int i, int opcnt,
2253   int c_mode)
2254 {
2255   if (po_test->operand_cnt == 1 && po_test->operand[0].type == OPT_CONST)
2256     return -1;
2257
2258   for (; i < opcnt; i++) {
2259     if (is_any_opr_modified(po_test, &ops[i], c_mode))
2260       return i;
2261   }
2262
2263   return -1;
2264 }
2265
2266 // scan for po_test operand[0] modification in range given
2267 static int scan_for_mod_opr0(struct parsed_op *po_test,
2268   int i, int opcnt)
2269 {
2270   for (; i < opcnt; i++) {
2271     if (is_opr_modified(&po_test->operand[0], &ops[i]))
2272       return i;
2273   }
2274
2275   return -1;
2276 }
2277
2278 static int scan_for_flag_set(int i, int magic, int *branched,
2279   int *setters, int *setter_cnt)
2280 {
2281   struct label_ref *lr;
2282   int ret;
2283
2284   while (i >= 0) {
2285     if (ops[i].cc_scratch == magic) {
2286       ferr(&ops[i], "%s looped\n", __func__);
2287       return -1;
2288     }
2289     ops[i].cc_scratch = magic;
2290
2291     if (g_labels[i][0] != 0) {
2292       *branched = 1;
2293
2294       lr = &g_label_refs[i];
2295       for (; lr->next; lr = lr->next) {
2296         ret = scan_for_flag_set(lr->i, magic,
2297                 branched, setters, setter_cnt);
2298         if (ret < 0)
2299           return ret;
2300       }
2301
2302       if (i > 0 && LAST_OP(i - 1)) {
2303         i = lr->i;
2304         continue;
2305       }
2306       ret = scan_for_flag_set(lr->i, magic,
2307               branched, setters, setter_cnt);
2308       if (ret < 0)
2309         return ret;
2310     }
2311     i--;
2312
2313     if (ops[i].flags & OPF_FLAGS) {
2314       setters[*setter_cnt] = i;
2315       (*setter_cnt)++;
2316       return 0;
2317     }
2318
2319     if ((ops[i].flags & (OPF_JMP|OPF_CJMP)) == OPF_JMP)
2320       return -1;
2321   }
2322
2323   return -1;
2324 }
2325
2326 // scan back for cdq, if anything modifies edx, fail
2327 static int scan_for_cdq_edx(int i)
2328 {
2329   while (i >= 0) {
2330     if (g_labels[i][0] != 0) {
2331       if (g_label_refs[i].next != NULL)
2332         return -1;
2333       if (i > 0 && LAST_OP(i - 1)) {
2334         i = g_label_refs[i].i;
2335         continue;
2336       }
2337       return -1;
2338     }
2339     i--;
2340
2341     if (ops[i].op == OP_CDQ)
2342       return i;
2343
2344     if (ops[i].regmask_dst & (1 << xDX))
2345       return -1;
2346   }
2347
2348   return -1;
2349 }
2350
2351 static int scan_for_reg_clear(int i, int reg)
2352 {
2353   while (i >= 0) {
2354     if (g_labels[i][0] != 0) {
2355       if (g_label_refs[i].next != NULL)
2356         return -1;
2357       if (i > 0 && LAST_OP(i - 1)) {
2358         i = g_label_refs[i].i;
2359         continue;
2360       }
2361       return -1;
2362     }
2363     i--;
2364
2365     if (ops[i].op == OP_XOR
2366      && ops[i].operand[0].lmod == OPLM_DWORD
2367      && ops[i].operand[0].reg == ops[i].operand[1].reg
2368      && ops[i].operand[0].reg == reg)
2369       return i;
2370
2371     if (ops[i].regmask_dst & (1 << reg))
2372       return -1;
2373   }
2374
2375   return -1;
2376 }
2377
2378 // scan for positive, constant esp adjust
2379 static int scan_for_esp_adjust(int i, int opcnt, int *adj,
2380   int *multipath)
2381 {
2382   struct parsed_op *po;
2383   int first_pop = -1;
2384
2385   *adj = *multipath = 0;
2386
2387   for (; i < opcnt; i++) {
2388     po = &ops[i];
2389
2390     if (g_labels[i][0] != 0)
2391       *multipath = 1;
2392
2393     if (po->op == OP_ADD && po->operand[0].reg == xSP) {
2394       if (po->operand[1].type != OPT_CONST)
2395         ferr(&ops[i], "non-const esp adjust?\n");
2396       *adj += po->operand[1].val;
2397       if (*adj & 3)
2398         ferr(&ops[i], "unaligned esp adjust: %x\n", *adj);
2399       return i;
2400     }
2401     else if (po->op == OP_PUSH && !(po->flags & OPF_RMD)) {
2402       //if (first_pop == -1)
2403       //  first_pop = -2; // none
2404       *adj -= lmod_bytes(po, po->operand[0].lmod);
2405     }
2406     else if (po->op == OP_POP && !(po->flags & OPF_RMD)) {
2407       // seems like msvc only uses 'pop ecx' for stack realignment..
2408       if (po->operand[0].type != OPT_REG || po->operand[0].reg != xCX)
2409         break;
2410       if (first_pop == -1 && *adj >= 0)
2411         first_pop = i;
2412       *adj += lmod_bytes(po, po->operand[0].lmod);
2413     }
2414     else if (po->flags & (OPF_JMP|OPF_TAIL)) {
2415       if (po->op == OP_JMP && po->btj == NULL) {
2416         i = po->bt_i - 1;
2417         continue;
2418       }
2419       if (po->op != OP_CALL)
2420         break;
2421       if (po->operand[0].type != OPT_LABEL)
2422         break;
2423       if (po->pp != NULL && po->pp->is_stdcall)
2424         break;
2425     }
2426   }
2427
2428   if (first_pop >= 0) {
2429     // probably 'pop ecx' was used..
2430     return first_pop;
2431   }
2432
2433   return -1;
2434 }
2435
2436 static void scan_fwd_set_flags(int i, int opcnt, int magic, int flags)
2437 {
2438   struct parsed_op *po;
2439   int j;
2440
2441   if (i < 0)
2442     ferr(ops, "%s: followed bad branch?\n", __func__);
2443
2444   for (; i < opcnt; i++) {
2445     po = &ops[i];
2446     if (po->cc_scratch == magic)
2447       return;
2448     po->cc_scratch = magic;
2449     po->flags |= flags;
2450
2451     if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
2452       if (po->btj != NULL) {
2453         // jumptable
2454         for (j = 0; j < po->btj->count; j++)
2455           scan_fwd_set_flags(po->btj->d[j].bt_i, opcnt, magic, flags);
2456         return;
2457       }
2458
2459       scan_fwd_set_flags(po->bt_i, opcnt, magic, flags);
2460       if (!(po->flags & OPF_CJMP))
2461         return;
2462     }
2463     if (po->flags & OPF_TAIL)
2464       return;
2465   }
2466 }
2467
2468 static const struct parsed_proto *try_recover_pp(
2469   struct parsed_op *po, const struct parsed_opr *opr, int *search_instead)
2470 {
2471   const struct parsed_proto *pp = NULL;
2472   char buf[256];
2473   char *p;
2474
2475   // maybe an arg of g_func?
2476   if (opr->type == OPT_REGMEM && is_stack_access(po, opr))
2477   {
2478     char ofs_reg[16] = { 0, };
2479     int arg, arg_s, arg_i;
2480     int stack_ra = 0;
2481     int offset = 0;
2482
2483     parse_stack_access(po, opr->name, ofs_reg,
2484       &offset, &stack_ra, NULL, 0);
2485     if (ofs_reg[0] != 0)
2486       ferr(po, "offset reg on arg access?\n");
2487     if (offset <= stack_ra) {
2488       // search who set the stack var instead
2489       if (search_instead != NULL)
2490         *search_instead = 1;
2491       return NULL;
2492     }
2493
2494     arg_i = (offset - stack_ra - 4) / 4;
2495     for (arg = arg_s = 0; arg < g_func_pp->argc; arg++) {
2496       if (g_func_pp->arg[arg].reg != NULL)
2497         continue;
2498       if (arg_s == arg_i)
2499         break;
2500       arg_s++;
2501     }
2502     if (arg == g_func_pp->argc)
2503       ferr(po, "stack arg %d not in prototype?\n", arg_i);
2504
2505     pp = g_func_pp->arg[arg].fptr;
2506     if (pp == NULL)
2507       ferr(po, "icall sa: arg%d is not a fptr?\n", arg + 1);
2508     check_func_pp(po, pp, "icall arg");
2509   }
2510   else if (opr->type == OPT_REGMEM && strchr(opr->name + 1, '[')) {
2511     // label[index]
2512     p = strchr(opr->name + 1, '[');
2513     memcpy(buf, opr->name, p - opr->name);
2514     buf[p - opr->name] = 0;
2515     pp = proto_parse(g_fhdr, buf, 0);
2516   }
2517   else if (opr->type == OPT_OFFSET || opr->type == OPT_LABEL) {
2518     pp = proto_parse(g_fhdr, opr->name, 0);
2519     if (pp == NULL)
2520       ferr(po, "proto_parse failed for icall from '%s'\n", opr->name);
2521     check_func_pp(po, pp, "reg-fptr ref");
2522   }
2523
2524   return pp;
2525 }
2526
2527 static void scan_for_call_type(int i, const struct parsed_opr *opr,
2528   int magic, const struct parsed_proto **pp_found, int *multi)
2529 {
2530   const struct parsed_proto *pp = NULL;
2531   struct parsed_op *po;
2532   struct label_ref *lr;
2533
2534   ops[i].cc_scratch = magic;
2535
2536   while (1) {
2537     if (g_labels[i][0] != 0) {
2538       lr = &g_label_refs[i];
2539       for (; lr != NULL; lr = lr->next)
2540         scan_for_call_type(lr->i, opr, magic, pp_found, multi);
2541       if (i > 0 && LAST_OP(i - 1))
2542         return;
2543     }
2544
2545     i--;
2546     if (i < 0)
2547       break;
2548
2549     if (ops[i].cc_scratch == magic)
2550       return;
2551     ops[i].cc_scratch = magic;
2552
2553     if (!(ops[i].flags & OPF_DATA))
2554       continue;
2555     if (!is_opr_modified(opr, &ops[i]))
2556       continue;
2557     if (ops[i].op != OP_MOV && ops[i].op != OP_LEA) {
2558       // most probably trashed by some processing
2559       *pp_found = NULL;
2560       return;
2561     }
2562
2563     opr = &ops[i].operand[1];
2564     if (opr->type != OPT_REG)
2565       break;
2566   }
2567
2568   po = (i >= 0) ? &ops[i] : ops;
2569
2570   if (i < 0) {
2571     // reached the top - can only be an arg-reg
2572     if (opr->type != OPT_REG)
2573       return;
2574
2575     for (i = 0; i < g_func_pp->argc; i++) {
2576       if (g_func_pp->arg[i].reg == NULL)
2577         continue;
2578       if (IS(opr->name, g_func_pp->arg[i].reg))
2579         break;
2580     }
2581     if (i == g_func_pp->argc)
2582       return;
2583     pp = g_func_pp->arg[i].fptr;
2584     if (pp == NULL)
2585       ferr(po, "icall: arg%d (%s) is not a fptr?\n",
2586         i + 1, g_func_pp->arg[i].reg);
2587     check_func_pp(po, pp, "icall reg-arg");
2588   }
2589   else
2590     pp = try_recover_pp(po, opr, NULL);
2591
2592   if (*pp_found != NULL && pp != NULL && *pp_found != pp) {
2593     if (!IS((*pp_found)->ret_type.name, pp->ret_type.name)
2594       || (*pp_found)->is_stdcall != pp->is_stdcall
2595       || (*pp_found)->is_fptr != pp->is_fptr
2596       || (*pp_found)->argc != pp->argc
2597       || (*pp_found)->argc_reg != pp->argc_reg
2598       || (*pp_found)->argc_stack != pp->argc_stack)
2599     {
2600       ferr(po, "icall: parsed_proto mismatch\n");
2601     }
2602     *multi = 1;
2603   }
2604   if (pp != NULL)
2605     *pp_found = pp;
2606 }
2607
2608 static const struct parsed_proto *resolve_icall(int i, int opcnt,
2609   int *multi_src)
2610 {
2611   const struct parsed_proto *pp = NULL;
2612   int search_advice = 0;
2613
2614   *multi_src = 0;
2615
2616   switch (ops[i].operand[0].type) {
2617   case OPT_REGMEM:
2618   case OPT_LABEL:
2619   case OPT_OFFSET:
2620     pp = try_recover_pp(&ops[i], &ops[i].operand[0], &search_advice);
2621     if (!search_advice)
2622       break;
2623     // fallthrough
2624   default:
2625     scan_for_call_type(i, &ops[i].operand[0], i + opcnt * 9, &pp,
2626       multi_src);
2627     break;
2628   }
2629
2630   return pp;
2631 }
2632
2633 // find an instruction that changed opr before i op
2634 // *op_i must be set to -1
2635 static int resolve_origin(int i, const struct parsed_opr *opr,
2636   int magic, int *op_i)
2637 {
2638   struct label_ref *lr;
2639   int ret = 0;
2640
2641   ops[i].cc_scratch = magic;
2642
2643   while (1) {
2644     if (g_labels[i][0] != 0) {
2645       lr = &g_label_refs[i];
2646       for (; lr != NULL; lr = lr->next)
2647         ret |= resolve_origin(lr->i, opr, magic, op_i);
2648       if (i > 0 && LAST_OP(i - 1))
2649         return ret;
2650     }
2651
2652     i--;
2653     if (i < 0)
2654       return -1;
2655
2656     if (ops[i].cc_scratch == magic)
2657       return 0;
2658     ops[i].cc_scratch = magic;
2659
2660     if (!(ops[i].flags & OPF_DATA))
2661       continue;
2662     if (!is_opr_modified(opr, &ops[i]))
2663       continue;
2664
2665     if (*op_i >= 0) {
2666       if (*op_i == i)
2667         return 1;
2668       // XXX: could check if the other op does the same
2669       return -1;
2670     }
2671
2672     *op_i = i;
2673     return 1;
2674   }
2675 }
2676
2677 static int try_resolve_const(int i, const struct parsed_opr *opr,
2678   int magic, unsigned int *val)
2679 {
2680   int s_i = -1;
2681   int ret = 0;
2682
2683   ret = resolve_origin(i, opr, magic, &s_i);
2684   if (ret == 1) {
2685     i = s_i;
2686     if (ops[i].op != OP_MOV && ops[i].operand[1].type != OPT_CONST)
2687       return -1;
2688
2689     *val = ops[i].operand[1].val;
2690     return 1;
2691   }
2692
2693   return -1;
2694 }
2695
2696 static int collect_call_args_r(struct parsed_op *po, int i,
2697   struct parsed_proto *pp, int *regmask, int *save_arg_vars, int arg,
2698   int magic, int need_op_saving, int may_reuse)
2699 {
2700   struct parsed_proto *pp_tmp;
2701   struct label_ref *lr;
2702   int need_to_save_current;
2703   int save_args;
2704   int ret = 0;
2705   int reg;
2706   char buf[32];
2707   int j, k;
2708
2709   if (i < 0) {
2710     ferr(po, "dead label encountered\n");
2711     return -1;
2712   }
2713
2714   for (; arg < pp->argc; arg++)
2715     if (pp->arg[arg].reg == NULL)
2716       break;
2717   magic = (magic & 0xffffff) | (arg << 24);
2718
2719   for (j = i; j >= 0 && (arg < pp->argc || pp->is_unresolved); )
2720   {
2721     if (((ops[j].cc_scratch ^ magic) & 0xffffff) == 0) {
2722       if (ops[j].cc_scratch != magic) {
2723         ferr(&ops[j], "arg collect hit same path with diff args for %s\n",
2724            pp->name);
2725         return -1;
2726       }
2727       // ok: have already been here
2728       return 0;
2729     }
2730     ops[j].cc_scratch = magic;
2731
2732     if (g_labels[j][0] != 0 && g_label_refs[j].i != -1) {
2733       lr = &g_label_refs[j];
2734       if (lr->next != NULL)
2735         need_op_saving = 1;
2736       for (; lr->next; lr = lr->next) {
2737         if ((ops[lr->i].flags & (OPF_JMP|OPF_CJMP)) != OPF_JMP)
2738           may_reuse = 1;
2739         ret = collect_call_args_r(po, lr->i, pp, regmask, save_arg_vars,
2740                 arg, magic, need_op_saving, may_reuse);
2741         if (ret < 0)
2742           return ret;
2743       }
2744
2745       if ((ops[lr->i].flags & (OPF_JMP|OPF_CJMP)) != OPF_JMP)
2746         may_reuse = 1;
2747       if (j > 0 && LAST_OP(j - 1)) {
2748         // follow last branch in reverse
2749         j = lr->i;
2750         continue;
2751       }
2752       need_op_saving = 1;
2753       ret = collect_call_args_r(po, lr->i, pp, regmask, save_arg_vars,
2754                arg, magic, need_op_saving, may_reuse);
2755       if (ret < 0)
2756         return ret;
2757     }
2758     j--;
2759
2760     if (ops[j].op == OP_CALL)
2761     {
2762       if (pp->is_unresolved)
2763         break;
2764
2765       pp_tmp = ops[j].pp;
2766       if (pp_tmp == NULL)
2767         ferr(po, "arg collect hit unparsed call '%s'\n",
2768           ops[j].operand[0].name);
2769       if (may_reuse && pp_tmp->argc_stack > 0)
2770         ferr(po, "arg collect %d/%d hit '%s' with %d stack args\n",
2771           arg, pp->argc, opr_name(&ops[j], 0), pp_tmp->argc_stack);
2772     }
2773     // esp adjust of 0 means we collected it before
2774     else if (ops[j].op == OP_ADD && ops[j].operand[0].reg == xSP
2775       && (ops[j].operand[1].type != OPT_CONST
2776           || ops[j].operand[1].val != 0))
2777     {
2778       if (pp->is_unresolved)
2779         break;
2780
2781       ferr(po, "arg collect %d/%d hit esp adjust of %d\n",
2782         arg, pp->argc, ops[j].operand[1].val);
2783     }
2784     else if (ops[j].op == OP_POP) {
2785       if (pp->is_unresolved)
2786         break;
2787
2788       ferr(po, "arg collect %d/%d hit pop\n", arg, pp->argc);
2789     }
2790     else if (ops[j].flags & OPF_CJMP)
2791     {
2792       if (pp->is_unresolved)
2793         break;
2794
2795       may_reuse = 1;
2796     }
2797     else if (ops[j].op == OP_PUSH && !(ops[j].flags & OPF_FARG))
2798     {
2799       if (pp->is_unresolved && (ops[j].flags & OPF_RMD))
2800         break;
2801
2802       pp->arg[arg].datap = &ops[j];
2803       need_to_save_current = 0;
2804       save_args = 0;
2805       reg = -1;
2806       if (ops[j].operand[0].type == OPT_REG)
2807         reg = ops[j].operand[0].reg;
2808
2809       if (!need_op_saving) {
2810         ret = scan_for_mod(&ops[j], j + 1, i, 1);
2811         need_to_save_current = (ret >= 0);
2812       }
2813       if (need_op_saving || need_to_save_current) {
2814         // mark this push as one that needs operand saving
2815         ops[j].flags &= ~OPF_RMD;
2816         if (ops[j].p_argnum == 0) {
2817           ops[j].p_argnum = arg + 1;
2818           save_args |= 1 << arg;
2819         }
2820         else if (ops[j].p_argnum < arg + 1) {
2821           // XXX: might kill valid var..
2822           //*save_arg_vars &= ~(1 << (ops[j].p_argnum - 1));
2823           ops[j].p_argnum = arg + 1;
2824           save_args |= 1 << arg;
2825         }
2826       }
2827       else if (ops[j].p_argnum == 0)
2828         ops[j].flags |= OPF_RMD;
2829
2830       // some PUSHes are reused by different calls on other branches,
2831       // but that can't happen if we didn't branch, so they
2832       // can be removed from future searches (handles nested calls)
2833       if (!may_reuse)
2834         ops[j].flags |= OPF_FARG;
2835
2836       ops[j].flags &= ~OPF_RSAVE;
2837
2838       // check for __VALIST
2839       if (!pp->is_unresolved && pp->arg[arg].type.is_va_list) {
2840         k = -1;
2841         ret = resolve_origin(j, &ops[j].operand[0], magic + 1, &k);
2842         if (ret == 1 && k >= 0)
2843         {
2844           if (ops[k].op == OP_LEA) {
2845             snprintf(buf, sizeof(buf), "arg_%X",
2846               g_func_pp->argc_stack * 4);
2847             if (!g_func_pp->is_vararg
2848               || strstr(ops[k].operand[1].name, buf))
2849             {
2850               ops[k].flags |= OPF_RMD;
2851               ops[j].flags |= OPF_RMD | OPF_VAPUSH;
2852               save_args &= ~(1 << arg);
2853               reg = -1;
2854             }
2855             else
2856               ferr(&ops[j], "lea va_list used, but no vararg?\n");
2857           }
2858           // check for va_list from g_func_pp arg too
2859           else if (ops[k].op == OP_MOV
2860             && is_stack_access(&ops[k], &ops[k].operand[1]))
2861           {
2862             ret = stack_frame_access(&ops[k], &ops[k].operand[1],
2863               buf, sizeof(buf), ops[k].operand[1].name, "", 1, 0);
2864             if (ret >= 0) {
2865               ops[k].flags |= OPF_RMD;
2866               ops[j].flags |= OPF_RMD;
2867               ops[j].p_argpass = ret + 1;
2868               save_args &= ~(1 << arg);
2869               reg = -1;
2870             }
2871           }
2872         }
2873       }
2874
2875       *save_arg_vars |= save_args;
2876
2877       // tracking reg usage
2878       if (reg >= 0)
2879         *regmask |= 1 << reg;
2880
2881       arg++;
2882       if (!pp->is_unresolved) {
2883         // next arg
2884         for (; arg < pp->argc; arg++)
2885           if (pp->arg[arg].reg == NULL)
2886             break;
2887       }
2888       magic = (magic & 0xffffff) | (arg << 24);
2889     }
2890   }
2891
2892   if (arg < pp->argc) {
2893     ferr(po, "arg collect failed for '%s': %d/%d\n",
2894       pp->name, arg, pp->argc);
2895     return -1;
2896   }
2897
2898   return arg;
2899 }
2900
2901 static int collect_call_args(struct parsed_op *po, int i,
2902   struct parsed_proto *pp, int *regmask, int *save_arg_vars,
2903   int magic)
2904 {
2905   int ret;
2906   int a;
2907
2908   ret = collect_call_args_r(po, i, pp, regmask, save_arg_vars,
2909           0, magic, 0, 0);
2910   if (ret < 0)
2911     return ret;
2912
2913   if (pp->is_unresolved) {
2914     pp->argc += ret;
2915     pp->argc_stack += ret;
2916     for (a = 0; a < pp->argc; a++)
2917       if (pp->arg[a].type.name == NULL)
2918         pp->arg[a].type.name = strdup("int");
2919   }
2920
2921   return ret;
2922 }
2923
2924 // early check for tail call or branch back
2925 static int is_like_tailjmp(int j)
2926 {
2927   if (!(ops[j].flags & OPF_JMP))
2928     return 0;
2929
2930   if (ops[j].op == OP_JMP && !ops[j].operand[0].had_ds)
2931     // probably local branch back..
2932     return 1;
2933   if (ops[j].op == OP_CALL)
2934     // probably noreturn call..
2935     return 1;
2936
2937   return 0;
2938 }
2939
2940 static void pp_insert_reg_arg(struct parsed_proto *pp, const char *reg)
2941 {
2942   int i;
2943
2944   for (i = 0; i < pp->argc; i++)
2945     if (pp->arg[i].reg == NULL)
2946       break;
2947
2948   if (pp->argc_stack)
2949     memmove(&pp->arg[i + 1], &pp->arg[i],
2950       sizeof(pp->arg[0]) * pp->argc_stack);
2951   memset(&pp->arg[i], 0, sizeof(pp->arg[i]));
2952   pp->arg[i].reg = strdup(reg);
2953   pp->arg[i].type.name = strdup("int");
2954   pp->argc++;
2955   pp->argc_reg++;
2956 }
2957
2958 static void add_label_ref(struct label_ref *lr, int op_i)
2959 {
2960   struct label_ref *lr_new;
2961
2962   if (lr->i == -1) {
2963     lr->i = op_i;
2964     return;
2965   }
2966
2967   lr_new = calloc(1, sizeof(*lr_new));
2968   lr_new->i = op_i;
2969   lr_new->next = lr->next;
2970   lr->next = lr_new;
2971 }
2972
2973 static void output_std_flags(FILE *fout, struct parsed_op *po,
2974   int *pfomask, const char *dst_opr_text)
2975 {
2976   if (*pfomask & (1 << PFO_Z)) {
2977     fprintf(fout, "\n  cond_z = (%s%s == 0);",
2978       lmod_cast_u(po, po->operand[0].lmod), dst_opr_text);
2979     *pfomask &= ~(1 << PFO_Z);
2980   }
2981   if (*pfomask & (1 << PFO_S)) {
2982     fprintf(fout, "\n  cond_s = (%s%s < 0);",
2983       lmod_cast_s(po, po->operand[0].lmod), dst_opr_text);
2984     *pfomask &= ~(1 << PFO_S);
2985   }
2986 }
2987
2988 static void output_pp_attrs(FILE *fout, const struct parsed_proto *pp,
2989   int is_noreturn)
2990 {
2991   if (pp->is_fastcall)
2992     fprintf(fout, "__fastcall ");
2993   else if (pp->is_stdcall && pp->argc_reg == 0)
2994     fprintf(fout, "__stdcall ");
2995   if (pp->is_noreturn || is_noreturn)
2996     fprintf(fout, "noreturn ");
2997 }
2998
2999 static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
3000 {
3001   struct parsed_op *po, *delayed_flag_op = NULL, *tmp_op;
3002   struct parsed_opr *last_arith_dst = NULL;
3003   char buf1[256], buf2[256], buf3[256], cast[64];
3004   const struct parsed_proto *pp_c;
3005   struct parsed_proto *pp, *pp_tmp;
3006   struct parsed_data *pd;
3007   const char *tmpname;
3008   unsigned int uval;
3009   int save_arg_vars = 0;
3010   int cond_vars = 0;
3011   int need_tmp_var = 0;
3012   int need_tmp64 = 0;
3013   int had_decl = 0;
3014   int label_pending = 0;
3015   int regmask_save = 0;
3016   int regmask_arg = 0;
3017   int regmask_now = 0;
3018   int regmask_init = 0;
3019   int regmask = 0;
3020   int pfomask = 0;
3021   int found = 0;
3022   int depth = 0;
3023   int no_output;
3024   int i, j, l;
3025   int arg;
3026   int reg;
3027   int ret;
3028
3029   g_bp_frame = g_sp_frame = g_stack_fsz = 0;
3030   g_stack_frame_used = 0;
3031
3032   g_func_pp = proto_parse(fhdr, funcn, 0);
3033   if (g_func_pp == NULL)
3034     ferr(ops, "proto_parse failed for '%s'\n", funcn);
3035
3036   for (i = 0; i < g_func_pp->argc; i++) {
3037     if (g_func_pp->arg[i].reg != NULL) {
3038       reg = char_array_i(regs_r32,
3039               ARRAY_SIZE(regs_r32), g_func_pp->arg[i].reg);
3040       if (reg < 0)
3041         ferr(ops, "arg '%s' is not a reg?\n", g_func_pp->arg[i].reg);
3042       regmask_arg |= 1 << reg;
3043     }
3044   }
3045
3046   // pass1:
3047   // - handle ebp/esp frame, remove ops related to it
3048   if (ops[0].op == OP_PUSH && IS(opr_name(&ops[0], 0), "ebp")
3049       && ops[1].op == OP_MOV
3050       && IS(opr_name(&ops[1], 0), "ebp")
3051       && IS(opr_name(&ops[1], 1), "esp"))
3052   {
3053     int ecx_push = 0;
3054
3055     g_bp_frame = 1;
3056     ops[0].flags |= OPF_RMD;
3057     ops[1].flags |= OPF_RMD;
3058     i = 2;
3059
3060     if (ops[2].op == OP_SUB && IS(opr_name(&ops[2], 0), "esp")) {
3061       g_stack_fsz = opr_const(&ops[2], 1);
3062       ops[2].flags |= OPF_RMD;
3063       i++;
3064     }
3065     else {
3066       // another way msvc builds stack frame..
3067       i = 2;
3068       while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
3069         g_stack_fsz += 4;
3070         ops[i].flags |= OPF_RMD;
3071         ecx_push++;
3072         i++;
3073       }
3074       // and another way..
3075       if (i == 2 && ops[i].op == OP_MOV && ops[i].operand[0].reg == xAX
3076           && ops[i].operand[1].type == OPT_CONST
3077           && ops[i + 1].op == OP_CALL
3078           && IS(opr_name(&ops[i + 1], 0), "__alloca_probe"))
3079       {
3080         g_stack_fsz += ops[i].operand[1].val;
3081         ops[i].flags |= OPF_RMD;
3082         i++;
3083         ops[i].flags |= OPF_RMD;
3084         i++;
3085       }
3086     }
3087
3088     found = 0;
3089     do {
3090       for (; i < opcnt; i++)
3091         if (ops[i].op == OP_RET)
3092           break;
3093       j = i - 1;
3094       if (i == opcnt && (ops[j].flags & OPF_JMP)) {
3095         if (found && is_like_tailjmp(j))
3096             break;
3097         j--;
3098       }
3099
3100       if ((ops[j].op == OP_POP && IS(opr_name(&ops[j], 0), "ebp"))
3101           || ops[j].op == OP_LEAVE)
3102       {
3103         ops[j].flags |= OPF_RMD;
3104       }
3105       else if (!(g_ida_func_attr & IDAFA_NORETURN))
3106         ferr(&ops[j], "'pop ebp' expected\n");
3107
3108       if (g_stack_fsz != 0) {
3109         if (ops[j - 1].op == OP_MOV
3110             && IS(opr_name(&ops[j - 1], 0), "esp")
3111             && IS(opr_name(&ops[j - 1], 1), "ebp"))
3112         {
3113           ops[j - 1].flags |= OPF_RMD;
3114         }
3115         else if (ops[j].op != OP_LEAVE
3116           && !(g_ida_func_attr & IDAFA_NORETURN))
3117         {
3118           ferr(&ops[j - 1], "esp restore expected\n");
3119         }
3120
3121         if (ecx_push && ops[j - 2].op == OP_POP
3122           && IS(opr_name(&ops[j - 2], 0), "ecx"))
3123         {
3124           ferr(&ops[j - 2], "unexpected ecx pop\n");
3125         }
3126       }
3127
3128       found = 1;
3129       i++;
3130     } while (i < opcnt);
3131   }
3132   else {
3133     int ecx_push = 0, esp_sub = 0;
3134
3135     i = 0;
3136     while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
3137       ops[i].flags |= OPF_RMD;
3138       g_stack_fsz += 4;
3139       ecx_push++;
3140       i++;
3141     }
3142
3143     for (; i < opcnt; i++) {
3144       if (ops[i].op == OP_PUSH || (ops[i].flags & (OPF_JMP|OPF_TAIL)))
3145         break;
3146       if (ops[i].op == OP_SUB && ops[i].operand[0].reg == xSP
3147         && ops[i].operand[1].type == OPT_CONST)
3148       {
3149         g_stack_fsz = ops[i].operand[1].val;
3150         ops[i].flags |= OPF_RMD;
3151         esp_sub = 1;
3152         break;
3153       }
3154     }
3155
3156     found = 0;
3157     if (ecx_push || esp_sub)
3158     {
3159       g_sp_frame = 1;
3160
3161       i++;
3162       do {
3163         for (; i < opcnt; i++)
3164           if (ops[i].op == OP_RET)
3165             break;
3166         j = i - 1;
3167         if (i == opcnt && (ops[j].flags & OPF_JMP)) {
3168           if (found && is_like_tailjmp(j))
3169               break;
3170           j--;
3171         }
3172
3173         if (ecx_push > 0) {
3174           for (l = 0; l < ecx_push; l++) {
3175             if (ops[j].op != OP_POP
3176               || !IS(opr_name(&ops[j], 0), "ecx"))
3177             {
3178               ferr(&ops[j], "'pop ecx' expected\n");
3179             }
3180             ops[j].flags |= OPF_RMD;
3181             j--;
3182           }
3183
3184           found = 1;
3185         }
3186
3187         if (esp_sub) {
3188           if (ops[j].op != OP_ADD
3189               || !IS(opr_name(&ops[j], 0), "esp")
3190               || ops[j].operand[1].type != OPT_CONST
3191               || ops[j].operand[1].val != g_stack_fsz)
3192             ferr(&ops[j], "'add esp' expected\n");
3193           ops[j].flags |= OPF_RMD;
3194
3195           found = 1;
3196         }
3197
3198         i++;
3199       } while (i < opcnt);
3200     }
3201   }
3202
3203   // pass2:
3204   // - parse calls with labels
3205   // - resolve all branches
3206   for (i = 0; i < opcnt; i++)
3207   {
3208     po = &ops[i];
3209     po->bt_i = -1;
3210     po->btj = NULL;
3211
3212     if (po->flags & OPF_RMD)
3213       continue;
3214
3215     if (po->op == OP_CALL) {
3216       pp = NULL;
3217
3218       if (po->operand[0].type == OPT_LABEL) {
3219         tmpname = opr_name(po, 0);
3220         if (IS_START(tmpname, "loc_"))
3221           ferr(po, "call to loc_*\n");
3222         pp_c = proto_parse(fhdr, tmpname, 0);
3223         if (pp_c == NULL)
3224           ferr(po, "proto_parse failed for call '%s'\n", tmpname);
3225
3226         pp = proto_clone(pp_c);
3227         my_assert_not(pp, NULL);
3228       }
3229       else if (po->datap != NULL) {
3230         pp = calloc(1, sizeof(*pp));
3231         my_assert_not(pp, NULL);
3232
3233         ret = parse_protostr(po->datap, pp);
3234         if (ret < 0)
3235           ferr(po, "bad protostr supplied: %s\n", (char *)po->datap);
3236         free(po->datap);
3237         po->datap = NULL;
3238       }
3239
3240       if (pp != NULL) {
3241         if (pp->is_fptr)
3242           check_func_pp(po, pp, "fptr var call");
3243         if (pp->is_noreturn)
3244           po->flags |= OPF_TAIL;
3245       }
3246       po->pp = pp;
3247       continue;
3248     }
3249
3250     if (!(po->flags & OPF_JMP) || po->op == OP_RET)
3251       continue;
3252
3253     if (po->operand[0].type == OPT_REGMEM) {
3254       char *p = strchr(po->operand[0].name, '[');
3255       if (p == NULL)
3256         goto tailcall;
3257       ret = p - po->operand[0].name;
3258       strncpy(buf1, po->operand[0].name, ret);
3259       buf1[ret] = 0;
3260
3261       for (j = 0, pd = NULL; j < g_func_pd_cnt; j++) {
3262         if (IS(g_func_pd[j].label, buf1)) {
3263           pd = &g_func_pd[j];
3264           break;
3265         }
3266       }
3267       if (pd == NULL)
3268         //ferr(po, "label '%s' not parsed?\n", buf1);
3269         goto tailcall;
3270       if (pd->type != OPT_OFFSET)
3271         ferr(po, "label '%s' with non-offset data?\n", buf1);
3272
3273       // find all labels, link
3274       for (j = 0; j < pd->count; j++) {
3275         for (l = 0; l < opcnt; l++) {
3276           if (g_labels[l][0] && IS(g_labels[l], pd->d[j].u.label)) {
3277             add_label_ref(&g_label_refs[l], i);
3278             pd->d[j].bt_i = l;
3279             break;
3280           }
3281         }
3282       }
3283
3284       po->btj = pd;
3285       continue;
3286     }
3287
3288     for (l = 0; l < opcnt; l++) {
3289       if (g_labels[l][0] && IS(po->operand[0].name, g_labels[l])) {
3290         if (l == i + 1 && po->op == OP_JMP) {
3291           // yet another alignment type..
3292           po->flags |= OPF_RMD;
3293           break;
3294         }
3295         add_label_ref(&g_label_refs[l], i);
3296         po->bt_i = l;
3297         break;
3298       }
3299     }
3300
3301     if (po->bt_i != -1 || (po->flags & OPF_RMD))
3302       continue;
3303
3304     if (po->operand[0].type == OPT_LABEL)
3305       // assume tail call
3306       goto tailcall;
3307
3308     ferr(po, "unhandled branch\n");
3309
3310 tailcall:
3311     po->op = OP_CALL;
3312     po->flags |= OPF_TAIL;
3313     if (i > 0 && ops[i - 1].op == OP_POP)
3314       po->flags |= OPF_ATAIL;
3315     i--; // reprocess
3316   }
3317
3318   // pass3:
3319   // - remove dead labels
3320   // - process calls
3321   for (i = 0; i < opcnt; i++)
3322   {
3323     if (g_labels[i][0] != 0 && g_label_refs[i].i == -1)
3324       g_labels[i][0] = 0;
3325
3326     po = &ops[i];
3327     if (po->flags & OPF_RMD)
3328       continue;
3329
3330     if (po->op == OP_CALL)
3331     {
3332       tmpname = opr_name(po, 0);
3333       pp = po->pp;
3334       if (pp == NULL)
3335       {
3336         // indirect call
3337         pp_c = resolve_icall(i, opcnt, &l);
3338         if (pp_c != NULL) {
3339           if (!pp_c->is_func && !pp_c->is_fptr)
3340             ferr(po, "call to non-func: %s\n", pp_c->name);
3341           pp = proto_clone(pp_c);
3342           my_assert_not(pp, NULL);
3343           if (l)
3344             // not resolved just to single func
3345             pp->is_fptr = 1;
3346
3347           switch (po->operand[0].type) {
3348           case OPT_REG:
3349             // we resolved this call and no longer need the register
3350             po->regmask_src &= ~(1 << po->operand[0].reg);
3351             break;
3352           case OPT_REGMEM:
3353             pp->is_fptr = 1;
3354             break;
3355           default:
3356             break;
3357           }
3358         }
3359         if (pp == NULL) {
3360           pp = calloc(1, sizeof(*pp));
3361           my_assert_not(pp, NULL);
3362           pp->is_fptr = 1;
3363           ret = scan_for_esp_adjust(i + 1, opcnt, &j, &l);
3364           if (ret < 0) {
3365             if (!g_allow_regfunc)
3366               ferr(po, "non-__cdecl indirect call unhandled yet\n");
3367             pp->is_unresolved = 1;
3368             j = 0;
3369           }
3370           j /= 4;
3371           if (j > ARRAY_SIZE(pp->arg))
3372             ferr(po, "esp adjust too large: %d\n", j);
3373           pp->ret_type.name = strdup("int");
3374           pp->argc = pp->argc_stack = j;
3375           for (arg = 0; arg < pp->argc; arg++)
3376             pp->arg[arg].type.name = strdup("int");
3377         }
3378         po->pp = pp;
3379       }
3380
3381       // look for and make use of esp adjust
3382       ret = -1;
3383       if (!pp->is_stdcall && pp->argc_stack > 0)
3384         ret = scan_for_esp_adjust(i + 1, opcnt, &j, &l);
3385       if (ret >= 0) {
3386         if (pp->is_vararg) {
3387           if (j / 4 < pp->argc_stack)
3388             ferr(po, "esp adjust is too small: %x < %x\n",
3389               j, pp->argc_stack * 4);
3390           // modify pp to make it have varargs as normal args
3391           arg = pp->argc;
3392           pp->argc += j / 4 - pp->argc_stack;
3393           for (; arg < pp->argc; arg++) {
3394             pp->arg[arg].type.name = strdup("int");
3395             pp->argc_stack++;
3396           }
3397           if (pp->argc > ARRAY_SIZE(pp->arg))
3398             ferr(po, "too many args for '%s'\n", tmpname);
3399         }
3400         if (pp->argc_stack != j / 4)
3401           ferr(po, "stack tracking failed for '%s': %x %x\n",
3402             tmpname, pp->argc_stack * 4, j);
3403
3404         ops[ret].flags |= OPF_RMD;
3405         if (ops[ret].op == OP_POP && j > 4) {
3406           // deal with multi-pop stack adjust
3407           j = pp->argc_stack;
3408           while (ops[ret].op == OP_POP && j > 0 && ret < opcnt) {
3409             ops[ret].flags |= OPF_RMD;
3410             j--;
3411             ret++;
3412           }
3413         }
3414         else if (!l) {
3415           // a bit of a hack, but deals with use of
3416           // single adj for multiple calls
3417           ops[ret].operand[1].val -= j;
3418         }
3419       }
3420       else if (pp->is_vararg)
3421         ferr(po, "missing esp_adjust for vararg func '%s'\n",
3422           pp->name);
3423
3424       if (!pp->is_unresolved && !(po->flags & OPF_ATAIL)) {
3425         // since we know the args, collect them
3426         collect_call_args(po, i, pp, &regmask, &save_arg_vars,
3427           i + opcnt * 2);
3428       }
3429
3430       if (strstr(pp->ret_type.name, "int64"))
3431         need_tmp64 = 1;
3432     }
3433   }
3434
3435   // pass4:
3436   // - find POPs for PUSHes, rm both
3437   // - scan for STD/CLD, propagate DF
3438   // - scan for all used registers
3439   // - find flag set ops for their users
3440   // - do unreselved calls
3441   // - declare indirect functions
3442   for (i = 0; i < opcnt; i++) {
3443     po = &ops[i];
3444     if (po->flags & OPF_RMD)
3445       continue;
3446
3447     if (po->op == OP_PUSH && (po->flags & OPF_RSAVE)) {
3448       reg = po->operand[0].reg;
3449       if (!(regmask & (1 << reg)))
3450         // not a reg save after all, rerun scan_for_pop
3451         po->flags &= ~OPF_RSAVE;
3452       else
3453         regmask_save |= 1 << reg;
3454     }
3455
3456     if (po->op == OP_PUSH && po->p_argnum == 0
3457       && !(po->flags & OPF_RSAVE) && !g_func_pp->is_userstack)
3458     {
3459       if (po->operand[0].type == OPT_REG)
3460       {
3461         reg = po->operand[0].reg;
3462         if (reg < 0)
3463           ferr(po, "reg not set for push?\n");
3464
3465         depth = 0;
3466         ret = scan_for_pop(i + 1, opcnt,
3467                 po->operand[0].name, i + opcnt * 3, 0, &depth, 0);
3468         if (ret == 1) {
3469           if (depth > 1)
3470             ferr(po, "too much depth: %d\n", depth);
3471
3472           po->flags |= OPF_RMD;
3473           scan_for_pop(i + 1, opcnt, po->operand[0].name,
3474             i + opcnt * 4, 0, &depth, 1);
3475           continue;
3476         }
3477         ret = scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, 0);
3478         if (ret == 0) {
3479           arg = OPF_RMD;
3480           if (regmask & (1 << reg)) {
3481             if (regmask_save & (1 << reg))
3482               ferr(po, "%s already saved?\n", po->operand[0].name);
3483             arg = OPF_RSAVE;
3484           }
3485           po->flags |= arg;
3486           scan_for_pop_ret(i + 1, opcnt, po->operand[0].name, arg);
3487           continue;
3488         }
3489       }
3490       else if (po->operand[0].type == OPT_CONST) {
3491         for (j = i + 1; j < opcnt; j++) {
3492           if ((ops[j].flags & (OPF_JMP|OPF_TAIL|OPF_RSAVE))
3493             || ops[j].op == OP_PUSH || g_labels[i][0] != 0)
3494           {
3495             break;
3496           }
3497
3498           if (!(ops[j].flags & OPF_RMD) && ops[j].op == OP_POP)
3499           {
3500             po->flags |= OPF_RMD;
3501             ops[j].datap = po;
3502             break;
3503           }
3504         }
3505       }
3506     }
3507
3508     if (po->op == OP_STD) {
3509       po->flags |= OPF_DF | OPF_RMD;
3510       scan_propagate_df(i + 1, opcnt);
3511     }
3512
3513     regmask_now = po->regmask_src | po->regmask_dst;
3514     if (regmask_now & (1 << xBP)) {
3515       if (g_bp_frame && !(po->flags & OPF_EBP_S)) {
3516         if (po->regmask_dst & (1 << xBP))
3517           // compiler decided to drop bp frame and use ebp as scratch
3518           scan_fwd_set_flags(i + 1, opcnt, i + opcnt * 5, OPF_EBP_S);
3519         else
3520           regmask_now &= ~(1 << xBP);
3521       }
3522     }
3523
3524     regmask |= regmask_now;
3525
3526     if (po->flags & OPF_CC)
3527     {
3528       int setters[16], cnt = 0, branched = 0;
3529
3530       ret = scan_for_flag_set(i, i + opcnt * 6,
3531               &branched, setters, &cnt);
3532       if (ret < 0 || cnt <= 0)
3533         ferr(po, "unable to trace flag setter(s)\n");
3534       if (cnt > ARRAY_SIZE(setters))
3535         ferr(po, "too many flag setters\n");
3536
3537       for (j = 0; j < cnt; j++)
3538       {
3539         tmp_op = &ops[setters[j]]; // flag setter
3540         pfomask = 0;
3541
3542         // to get nicer code, we try to delay test and cmp;
3543         // if we can't because of operand modification, or if we
3544         // have arith op, or branch, make it calculate flags explicitly
3545         if (tmp_op->op == OP_TEST || tmp_op->op == OP_CMP)
3546         {
3547           if (branched || scan_for_mod(tmp_op, setters[j] + 1, i, 0) >= 0)
3548             pfomask = 1 << po->pfo;
3549         }
3550         else if (tmp_op->op == OP_CMPS || tmp_op->op == OP_SCAS) {
3551           pfomask = 1 << po->pfo;
3552         }
3553         else {
3554           // see if we'll be able to handle based on op result
3555           if ((tmp_op->op != OP_AND && tmp_op->op != OP_OR
3556                && po->pfo != PFO_Z && po->pfo != PFO_S
3557                && po->pfo != PFO_P)
3558               || branched
3559               || scan_for_mod_opr0(tmp_op, setters[j] + 1, i) >= 0)
3560           {
3561             pfomask = 1 << po->pfo;
3562           }
3563
3564           if (tmp_op->op == OP_ADD && po->pfo == PFO_C) {
3565             propagate_lmod(tmp_op, &tmp_op->operand[0],
3566               &tmp_op->operand[1]);
3567             if (tmp_op->operand[0].lmod == OPLM_DWORD)
3568               need_tmp64 = 1;
3569           }
3570         }
3571         if (pfomask) {
3572           tmp_op->pfomask |= pfomask;
3573           cond_vars |= pfomask;
3574         }
3575         // note: may overwrite, currently not a problem
3576         po->datap = tmp_op;
3577       }
3578
3579       if (po->op == OP_RCL || po->op == OP_RCR
3580        || po->op == OP_ADC || po->op == OP_SBB)
3581         cond_vars |= 1 << PFO_C;
3582     }
3583
3584     if (po->op == OP_CMPS || po->op == OP_SCAS) {
3585       cond_vars |= 1 << PFO_Z;
3586     }
3587     else if (po->op == OP_MUL
3588       || (po->op == OP_IMUL && po->operand_cnt == 1))
3589     {
3590       if (po->operand[0].lmod == OPLM_DWORD)
3591         need_tmp64 = 1;
3592     }
3593     else if (po->op == OP_CALL) {
3594       pp = po->pp;
3595       if (pp == NULL)
3596         ferr(po, "NULL pp\n");
3597
3598       if (pp->is_unresolved) {
3599         int regmask_stack = 0;
3600         collect_call_args(po, i, pp, &regmask, &save_arg_vars,
3601           i + opcnt * 2);
3602
3603         // this is pretty rough guess:
3604         // see ecx and edx were pushed (and not their saved versions)
3605         for (arg = 0; arg < pp->argc; arg++) {
3606           if (pp->arg[arg].reg != NULL)
3607             continue;
3608
3609           tmp_op = pp->arg[arg].datap;
3610           if (tmp_op == NULL)
3611             ferr(po, "parsed_op missing for arg%d\n", arg);
3612           if (tmp_op->p_argnum == 0 && tmp_op->operand[0].type == OPT_REG)
3613             regmask_stack |= 1 << tmp_op->operand[0].reg;
3614         }
3615
3616         if (!((regmask_stack & (1 << xCX))
3617           && (regmask_stack & (1 << xDX))))
3618         {
3619           if (pp->argc_stack != 0
3620            || ((regmask | regmask_arg) & ((1 << xCX)|(1 << xDX))))
3621           {
3622             pp_insert_reg_arg(pp, "ecx");
3623             pp->is_fastcall = 1;
3624             regmask_init |= 1 << xCX;
3625             regmask |= 1 << xCX;
3626           }
3627           if (pp->argc_stack != 0
3628            || ((regmask | regmask_arg) & (1 << xDX)))
3629           {
3630             pp_insert_reg_arg(pp, "edx");
3631             regmask_init |= 1 << xDX;
3632             regmask |= 1 << xDX;
3633           }
3634         }
3635
3636         // note: __cdecl doesn't fall into is_unresolved category
3637         if (pp->argc_stack > 0)
3638           pp->is_stdcall = 1;
3639       }
3640
3641       for (arg = 0; arg < pp->argc; arg++) {
3642         if (pp->arg[arg].reg != NULL) {
3643           reg = char_array_i(regs_r32,
3644                   ARRAY_SIZE(regs_r32), pp->arg[arg].reg);
3645           if (reg < 0)
3646             ferr(ops, "arg '%s' is not a reg?\n", pp->arg[arg].reg);
3647           if (!(regmask & (1 << reg))) {
3648             regmask_init |= 1 << reg;
3649             regmask |= 1 << reg;
3650           }
3651         }
3652       }
3653     }
3654     else if (po->op == OP_MOV && po->operand[0].pp != NULL
3655       && po->operand[1].pp != NULL)
3656     {
3657       // <var> = offset <something>
3658       if ((po->operand[1].pp->is_func || po->operand[1].pp->is_fptr)
3659         && !IS_START(po->operand[1].name, "off_"))
3660       {
3661         if (!po->operand[0].pp->is_fptr)
3662           ferr(po, "%s not declared as fptr when it should be\n",
3663             po->operand[0].name);
3664         if (pp_cmp_func(po->operand[0].pp, po->operand[1].pp)) {
3665           pp_print(buf1, sizeof(buf1), po->operand[0].pp);
3666           pp_print(buf2, sizeof(buf2), po->operand[1].pp);
3667           fnote(po, "var:  %s\n", buf1);
3668           fnote(po, "func: %s\n", buf2);
3669           ferr(po, "^ mismatch\n");
3670         }
3671       }
3672     }
3673     else if (po->op == OP_RET && !IS(g_func_pp->ret_type.name, "void"))
3674       regmask |= 1 << xAX;
3675     else if (po->op == OP_DIV || po->op == OP_IDIV) {
3676       // 32bit division is common, look for it
3677       if (po->op == OP_DIV)
3678         ret = scan_for_reg_clear(i, xDX);
3679       else
3680         ret = scan_for_cdq_edx(i);
3681       if (ret >= 0)
3682         po->flags |= OPF_32BIT;
3683       else
3684         need_tmp64 = 1;
3685     }
3686     else if (po->op == OP_CLD)
3687       po->flags |= OPF_RMD;
3688
3689     if (po->op == OP_RCL || po->op == OP_RCR || po->op == OP_XCHG) {
3690       need_tmp_var = 1;
3691     }
3692   }
3693
3694   // pass4:
3695   // - confirm regmask_save, it might have been reduced
3696   if (regmask_save != 0)
3697   {
3698     regmask_save = 0;
3699     for (i = 0; i < opcnt; i++) {
3700       po = &ops[i];
3701       if (po->flags & OPF_RMD)
3702         continue;
3703
3704       if (po->op == OP_PUSH && (po->flags & OPF_RSAVE))
3705         regmask_save |= 1 << po->operand[0].reg;
3706     }
3707   }
3708
3709   // output starts here
3710
3711   // define userstack size
3712   if (g_func_pp->is_userstack) {
3713     fprintf(fout, "#ifndef US_SZ_%s\n", g_func_pp->name);
3714     fprintf(fout, "#define US_SZ_%s USERSTACK_SIZE\n", g_func_pp->name);
3715     fprintf(fout, "#endif\n");
3716   }
3717
3718   // the function itself
3719   fprintf(fout, "%s ", g_func_pp->ret_type.name);
3720   output_pp_attrs(fout, g_func_pp, g_ida_func_attr & IDAFA_NORETURN);
3721   fprintf(fout, "%s(", g_func_pp->name);
3722
3723   for (i = 0; i < g_func_pp->argc; i++) {
3724     if (i > 0)
3725       fprintf(fout, ", ");
3726     if (g_func_pp->arg[i].fptr != NULL) {
3727       // func pointer..
3728       pp = g_func_pp->arg[i].fptr;
3729       fprintf(fout, "%s (", pp->ret_type.name);
3730       output_pp_attrs(fout, pp, 0);
3731       fprintf(fout, "*a%d)(", i + 1);
3732       for (j = 0; j < pp->argc; j++) {
3733         if (j > 0)
3734           fprintf(fout, ", ");
3735         if (pp->arg[j].fptr)
3736           ferr(ops, "nested fptr\n");
3737         fprintf(fout, "%s", pp->arg[j].type.name);
3738       }
3739       if (pp->is_vararg) {
3740         if (j > 0)
3741           fprintf(fout, ", ");
3742         fprintf(fout, "...");
3743       }
3744       fprintf(fout, ")");
3745     }
3746     else if (g_func_pp->arg[i].type.is_retreg) {
3747       fprintf(fout, "u32 *r_%s", g_func_pp->arg[i].reg);
3748     }
3749     else {
3750       fprintf(fout, "%s a%d", g_func_pp->arg[i].type.name, i + 1);
3751     }
3752   }
3753   if (g_func_pp->is_vararg) {
3754     if (i > 0)
3755       fprintf(fout, ", ");
3756     fprintf(fout, "...");
3757   }
3758
3759   fprintf(fout, ")\n{\n");
3760
3761   // declare indirect functions
3762   for (i = 0; i < opcnt; i++) {
3763     po = &ops[i];
3764     if (po->flags & OPF_RMD)
3765       continue;
3766
3767     if (po->op == OP_CALL) {
3768       pp = po->pp;
3769       if (pp == NULL)
3770         ferr(po, "NULL pp\n");
3771
3772       if (pp->is_fptr && !(pp->name[0] != 0 && pp->is_arg)) {
3773         if (pp->name[0] != 0) {
3774           memmove(pp->name + 2, pp->name, strlen(pp->name) + 1);
3775           memcpy(pp->name, "i_", 2);
3776
3777           // might be declared already
3778           found = 0;
3779           for (j = 0; j < i; j++) {
3780             if (ops[j].op == OP_CALL && (pp_tmp = ops[j].pp)) {
3781               if (pp_tmp->is_fptr && IS(pp->name, pp_tmp->name)) {
3782                 found = 1;
3783                 break;
3784               }
3785             }
3786           }
3787           if (found)
3788             continue;
3789         }
3790         else
3791           snprintf(pp->name, sizeof(pp->name), "icall%d", i);
3792
3793         fprintf(fout, "  %s (", pp->ret_type.name);
3794         output_pp_attrs(fout, pp, 0);
3795         fprintf(fout, "*%s)(", pp->name);
3796         for (j = 0; j < pp->argc; j++) {
3797           if (j > 0)
3798             fprintf(fout, ", ");
3799           fprintf(fout, "%s a%d", pp->arg[j].type.name, j + 1);
3800         }
3801         fprintf(fout, ");\n");
3802       }
3803     }
3804   }
3805
3806   // output LUTs/jumptables
3807   for (i = 0; i < g_func_pd_cnt; i++) {
3808     pd = &g_func_pd[i];
3809     fprintf(fout, "  static const ");
3810     if (pd->type == OPT_OFFSET) {
3811       fprintf(fout, "void *jt_%s[] =\n    { ", pd->label);
3812
3813       for (j = 0; j < pd->count; j++) {
3814         if (j > 0)
3815           fprintf(fout, ", ");
3816         fprintf(fout, "&&%s", pd->d[j].u.label);
3817       }
3818     }
3819     else {
3820       fprintf(fout, "%s %s[] =\n    { ",
3821         lmod_type_u(ops, pd->lmod), pd->label);
3822
3823       for (j = 0; j < pd->count; j++) {
3824         if (j > 0)
3825           fprintf(fout, ", ");
3826         fprintf(fout, "%u", pd->d[j].u.val);
3827       }
3828     }
3829     fprintf(fout, " };\n");
3830     had_decl = 1;
3831   }
3832
3833   // declare stack frame, va_arg
3834   if (g_stack_fsz) {
3835     fprintf(fout, "  union { u32 d[%d]; u16 w[%d]; u8 b[%d]; } sf;\n",
3836       (g_stack_fsz + 3) / 4, (g_stack_fsz + 1) / 2, g_stack_fsz);
3837     had_decl = 1;
3838   }
3839
3840   if (g_func_pp->is_userstack) {
3841     fprintf(fout, "  u32 fake_sf[US_SZ_%s / 4];\n", g_func_pp->name);
3842     fprintf(fout, "  u32 *esp = &fake_sf[sizeof(fake_sf) / 4];\n");
3843     had_decl = 1;
3844   }
3845
3846   if (g_func_pp->is_vararg) {
3847     fprintf(fout, "  va_list ap;\n");
3848     had_decl = 1;
3849   }
3850
3851   // declare arg-registers
3852   for (i = 0; i < g_func_pp->argc; i++) {
3853     if (g_func_pp->arg[i].reg != NULL) {
3854       reg = char_array_i(regs_r32,
3855               ARRAY_SIZE(regs_r32), g_func_pp->arg[i].reg);
3856       if (regmask & (1 << reg)) {
3857         if (g_func_pp->arg[i].type.is_retreg)
3858           fprintf(fout, "  u32 %s = *r_%s;\n",
3859             g_func_pp->arg[i].reg, g_func_pp->arg[i].reg);
3860         else
3861           fprintf(fout, "  u32 %s = (u32)a%d;\n",
3862             g_func_pp->arg[i].reg, i + 1);
3863       }
3864       else {
3865         if (g_func_pp->arg[i].type.is_retreg)
3866           ferr(ops, "retreg '%s' is unused?\n",
3867             g_func_pp->arg[i].reg);
3868         fprintf(fout, "  // %s = a%d; // unused\n",
3869           g_func_pp->arg[i].reg, i + 1);
3870       }
3871       had_decl = 1;
3872     }
3873   }
3874
3875   regmask_now = regmask & ~regmask_arg;
3876   regmask_now &= ~(1 << xSP);
3877   if (regmask_now) {
3878     for (reg = 0; reg < 8; reg++) {
3879       if (regmask_now & (1 << reg)) {
3880         fprintf(fout, "  u32 %s", regs_r32[reg]);
3881         if (regmask_init & (1 << reg))
3882           fprintf(fout, " = 0");
3883         fprintf(fout, ";\n");
3884         had_decl = 1;
3885       }
3886     }
3887   }
3888
3889   if (regmask_save) {
3890     for (reg = 0; reg < 8; reg++) {
3891       if (regmask_save & (1 << reg)) {
3892         fprintf(fout, "  u32 s_%s;\n", regs_r32[reg]);
3893         had_decl = 1;
3894       }
3895     }
3896   }
3897
3898   if (save_arg_vars) {
3899     for (reg = 0; reg < 32; reg++) {
3900       if (save_arg_vars & (1 << reg)) {
3901         fprintf(fout, "  u32 s_a%d;\n", reg + 1);
3902         had_decl = 1;
3903       }
3904     }
3905   }
3906
3907   if (cond_vars) {
3908     for (i = 0; i < 8; i++) {
3909       if (cond_vars & (1 << i)) {
3910         fprintf(fout, "  u32 cond_%s;\n", parsed_flag_op_names[i]);
3911         had_decl = 1;
3912       }
3913     }
3914   }
3915
3916   if (need_tmp_var) {
3917     fprintf(fout, "  u32 tmp;\n");
3918     had_decl = 1;
3919   }
3920
3921   if (need_tmp64) {
3922     fprintf(fout, "  u64 tmp64;\n");
3923     had_decl = 1;
3924   }
3925
3926   if (had_decl)
3927     fprintf(fout, "\n");
3928
3929   if (g_func_pp->is_vararg) {
3930     if (g_func_pp->argc_stack == 0)
3931       ferr(ops, "vararg func without stack args?\n");
3932     fprintf(fout, "  va_start(ap, a%d);\n", g_func_pp->argc);
3933   }
3934
3935   // output ops
3936   for (i = 0; i < opcnt; i++)
3937   {
3938     if (g_labels[i][0] != 0) {
3939       fprintf(fout, "\n%s:\n", g_labels[i]);
3940       label_pending = 1;
3941
3942       delayed_flag_op = NULL;
3943       last_arith_dst = NULL;
3944     }
3945
3946     po = &ops[i];
3947     if (po->flags & OPF_RMD)
3948       continue;
3949
3950     no_output = 0;
3951
3952     #define assert_operand_cnt(n_) \
3953       if (po->operand_cnt != n_) \
3954         ferr(po, "operand_cnt is %d/%d\n", po->operand_cnt, n_)
3955
3956     // conditional/flag using op?
3957     if (po->flags & OPF_CC)
3958     {
3959       int is_delayed = 0;
3960
3961       tmp_op = po->datap;
3962
3963       // we go through all this trouble to avoid using parsed_flag_op,
3964       // which makes generated code much nicer
3965       if (delayed_flag_op != NULL)
3966       {
3967         out_cmp_test(buf1, sizeof(buf1), delayed_flag_op,
3968           po->pfo, po->pfo_inv);
3969         is_delayed = 1;
3970       }
3971       else if (last_arith_dst != NULL
3972         && (po->pfo == PFO_Z || po->pfo == PFO_S || po->pfo == PFO_P
3973            || (tmp_op && (tmp_op->op == OP_AND || tmp_op->op == OP_OR))
3974            ))
3975       {
3976         out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst);
3977         out_test_for_cc(buf1, sizeof(buf1), po, po->pfo, po->pfo_inv,
3978           last_arith_dst->lmod, buf3);
3979         is_delayed = 1;
3980       }
3981       else if (tmp_op != NULL) {
3982         // use preprocessed flag calc results
3983         if (!(tmp_op->pfomask & (1 << po->pfo)))
3984           ferr(po, "not prepared for pfo %d\n", po->pfo);
3985
3986         // note: pfo_inv was not yet applied
3987         snprintf(buf1, sizeof(buf1), "(%scond_%s)",
3988           po->pfo_inv ? "!" : "", parsed_flag_op_names[po->pfo]);
3989       }
3990       else {
3991         ferr(po, "all methods of finding comparison failed\n");
3992       }
3993  
3994       if (po->flags & OPF_JMP) {
3995         fprintf(fout, "  if %s", buf1);
3996       }
3997       else if (po->op == OP_RCL || po->op == OP_RCR
3998                || po->op == OP_ADC || po->op == OP_SBB)
3999       {
4000         if (is_delayed)
4001           fprintf(fout, "  cond_%s = %s;\n",
4002             parsed_flag_op_names[po->pfo], buf1);
4003       }
4004       else if (po->flags & OPF_DATA) { // SETcc
4005         out_dst_opr(buf2, sizeof(buf2), po, &po->operand[0]);
4006         fprintf(fout, "  %s = %s;", buf2, buf1);
4007       }
4008       else {
4009         ferr(po, "unhandled conditional op\n");
4010       }
4011     }
4012
4013     pfomask = po->pfomask;
4014
4015     if (po->flags & (OPF_REPZ|OPF_REPNZ)) {
4016       struct parsed_opr opr = {0,};
4017       opr.type = OPT_REG;
4018       opr.reg = xCX;
4019       opr.lmod = OPLM_DWORD;
4020       ret = try_resolve_const(i, &opr, opcnt * 7 + i, &uval);
4021
4022       if (ret != 1 || uval == 0) {
4023         // we need initial flags for ecx=0 case..
4024         if (i > 0 && ops[i - 1].op == OP_XOR
4025           && IS(ops[i - 1].operand[0].name,
4026                 ops[i - 1].operand[1].name))
4027         {
4028           fprintf(fout, "  cond_z = ");
4029           if (pfomask & (1 << PFO_C))
4030             fprintf(fout, "cond_c = ");
4031           fprintf(fout, "0;\n");
4032         }
4033         else if (last_arith_dst != NULL) {
4034           out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst);
4035           out_test_for_cc(buf1, sizeof(buf1), po, PFO_Z, 0,
4036             last_arith_dst->lmod, buf3);
4037           fprintf(fout, "  cond_z = %s;\n", buf1);
4038         }
4039         else
4040           ferr(po, "missing initial ZF\n");
4041       }
4042     }
4043
4044     switch (po->op)
4045     {
4046       case OP_MOV:
4047         assert_operand_cnt(2);
4048         propagate_lmod(po, &po->operand[0], &po->operand[1]);
4049         out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
4050         default_cast_to(buf3, sizeof(buf3), &po->operand[0]);
4051         fprintf(fout, "  %s = %s;", buf1,
4052             out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
4053               buf3, 0));
4054         break;
4055
4056       case OP_LEA:
4057         assert_operand_cnt(2);
4058         po->operand[1].lmod = OPLM_DWORD; // always
4059         fprintf(fout, "  %s = %s;",
4060             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
4061             out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
4062               NULL, 1));
4063         break;
4064
4065       case OP_MOVZX:
4066         assert_operand_cnt(2);
4067         fprintf(fout, "  %s = %s;",
4068             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
4069             out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
4070         break;
4071
4072       case OP_MOVSX:
4073         assert_operand_cnt(2);
4074         switch (po->operand[1].lmod) {
4075         case OPLM_BYTE:
4076           strcpy(buf3, "(s8)");
4077           break;
4078         case OPLM_WORD:
4079           strcpy(buf3, "(s16)");
4080           break;
4081         default:
4082           ferr(po, "invalid src lmod: %d\n", po->operand[1].lmod);
4083         }
4084         fprintf(fout, "  %s = %s;",
4085             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
4086             out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
4087               buf3, 0));
4088         break;
4089
4090       case OP_XCHG:
4091         assert_operand_cnt(2);
4092         propagate_lmod(po, &po->operand[0], &po->operand[1]);
4093         fprintf(fout, "  tmp = %s;",
4094           out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], "", 0));
4095         fprintf(fout, " %s = %s;",
4096           out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
4097           out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
4098             default_cast_to(buf3, sizeof(buf3), &po->operand[0]), 0));
4099         fprintf(fout, " %s = %stmp;",
4100           out_dst_opr(buf1, sizeof(buf1), po, &po->operand[1]),
4101           default_cast_to(buf3, sizeof(buf3), &po->operand[1]));
4102         snprintf(g_comment, sizeof(g_comment), "xchg");
4103         break;
4104
4105       case OP_NOT:
4106         assert_operand_cnt(1);
4107         out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
4108         fprintf(fout, "  %s = ~%s;", buf1, buf1);
4109         break;
4110
4111       case OP_CDQ:
4112         assert_operand_cnt(2);
4113         fprintf(fout, "  %s = (s32)%s >> 31;",
4114             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
4115             out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
4116         strcpy(g_comment, "cdq");
4117         break;
4118
4119       case OP_LODS:
4120         assert_operand_cnt(3);
4121         if (po->flags & OPF_REP) {
4122           // hmh..
4123           ferr(po, "TODO\n");
4124         }
4125         else {
4126           fprintf(fout, "  eax = %sesi; esi %c= %d;",
4127             lmod_cast_u_ptr(po, po->operand[0].lmod),
4128             (po->flags & OPF_DF) ? '-' : '+',
4129             lmod_bytes(po, po->operand[0].lmod));
4130           strcpy(g_comment, "lods");
4131         }
4132         break;
4133
4134       case OP_STOS:
4135         assert_operand_cnt(3);
4136         if (po->flags & OPF_REP) {
4137           fprintf(fout, "  for (; ecx != 0; ecx--, edi %c= %d)\n",
4138             (po->flags & OPF_DF) ? '-' : '+',
4139             lmod_bytes(po, po->operand[0].lmod));
4140           fprintf(fout, "    %sedi = eax;",
4141             lmod_cast_u_ptr(po, po->operand[0].lmod));
4142           strcpy(g_comment, "rep stos");
4143         }
4144         else {
4145           fprintf(fout, "  %sedi = eax; edi %c= %d;",
4146             lmod_cast_u_ptr(po, po->operand[0].lmod),
4147             (po->flags & OPF_DF) ? '-' : '+',
4148             lmod_bytes(po, po->operand[0].lmod));
4149           strcpy(g_comment, "stos");
4150         }
4151         break;
4152
4153       case OP_MOVS:
4154         assert_operand_cnt(3);
4155         j = lmod_bytes(po, po->operand[0].lmod);
4156         strcpy(buf1, lmod_cast_u_ptr(po, po->operand[0].lmod));
4157         l = (po->flags & OPF_DF) ? '-' : '+';
4158         if (po->flags & OPF_REP) {
4159           fprintf(fout,
4160             "  for (; ecx != 0; ecx--, edi %c= %d, esi %c= %d)\n",
4161             l, j, l, j);
4162           fprintf(fout,
4163             "    %sedi = %sesi;", buf1, buf1);
4164           strcpy(g_comment, "rep movs");
4165         }
4166         else {
4167           fprintf(fout, "  %sedi = %sesi; edi %c= %d; esi %c= %d;",
4168             buf1, buf1, l, j, l, j);
4169           strcpy(g_comment, "movs");
4170         }
4171         break;
4172
4173       case OP_CMPS:
4174         // repe ~ repeat while ZF=1
4175         assert_operand_cnt(3);
4176         j = lmod_bytes(po, po->operand[0].lmod);
4177         strcpy(buf1, lmod_cast_u_ptr(po, po->operand[0].lmod));
4178         l = (po->flags & OPF_DF) ? '-' : '+';
4179         if (po->flags & OPF_REP) {
4180           fprintf(fout,
4181             "  for (; ecx != 0; ecx--) {\n");
4182           if (pfomask & (1 << PFO_C)) {
4183             // ugh..
4184             fprintf(fout,
4185             "    cond_c = %sesi < %sedi;\n", buf1, buf1);
4186             pfomask &= ~(1 << PFO_C);
4187           }
4188           fprintf(fout,
4189             "    cond_z = (%sesi == %sedi); esi %c= %d, edi %c= %d;\n",
4190               buf1, buf1, l, j, l, j);
4191           fprintf(fout,
4192             "    if (cond_z %s 0) break;\n",
4193               (po->flags & OPF_REPZ) ? "==" : "!=");
4194           fprintf(fout,
4195             "  }");
4196           snprintf(g_comment, sizeof(g_comment), "rep%s cmps",
4197             (po->flags & OPF_REPZ) ? "e" : "ne");
4198         }
4199         else {
4200           fprintf(fout,
4201             "  cond_z = (%sesi == %sedi); esi %c= %d; edi %c= %d;",
4202             buf1, buf1, l, j, l, j);
4203           strcpy(g_comment, "cmps");
4204         }
4205         pfomask &= ~(1 << PFO_Z);
4206         last_arith_dst = NULL;
4207         delayed_flag_op = NULL;
4208         break;
4209
4210       case OP_SCAS:
4211         // only does ZF (for now)
4212         // repe ~ repeat while ZF=1
4213         assert_operand_cnt(3);
4214         j = lmod_bytes(po, po->operand[0].lmod);
4215         l = (po->flags & OPF_DF) ? '-' : '+';
4216         if (po->flags & OPF_REP) {
4217           fprintf(fout,
4218             "  for (; ecx != 0; ecx--) {\n");
4219           fprintf(fout,
4220             "    cond_z = (%seax == %sedi); edi %c= %d;\n",
4221               lmod_cast_u(po, po->operand[0].lmod),
4222               lmod_cast_u_ptr(po, po->operand[0].lmod), l, j);
4223           fprintf(fout,
4224             "    if (cond_z %s 0) break;\n",
4225               (po->flags & OPF_REPZ) ? "==" : "!=");
4226           fprintf(fout,
4227             "  }");
4228           snprintf(g_comment, sizeof(g_comment), "rep%s scas",
4229             (po->flags & OPF_REPZ) ? "e" : "ne");
4230         }
4231         else {
4232           fprintf(fout, "  cond_z = (%seax == %sedi); edi %c= %d;",
4233               lmod_cast_u(po, po->operand[0].lmod),
4234               lmod_cast_u_ptr(po, po->operand[0].lmod), l, j);
4235           strcpy(g_comment, "scas");
4236         }
4237         pfomask &= ~(1 << PFO_Z);
4238         last_arith_dst = NULL;
4239         delayed_flag_op = NULL;
4240         break;
4241
4242       // arithmetic w/flags
4243       case OP_AND:
4244       case OP_OR:
4245         propagate_lmod(po, &po->operand[0], &po->operand[1]);
4246         // fallthrough
4247       dualop_arith:
4248         assert_operand_cnt(2);
4249         fprintf(fout, "  %s %s= %s;",
4250             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
4251             op_to_c(po),
4252             out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
4253         output_std_flags(fout, po, &pfomask, buf1);
4254         last_arith_dst = &po->operand[0];
4255         delayed_flag_op = NULL;
4256         break;
4257
4258       case OP_SHL:
4259       case OP_SHR:
4260         assert_operand_cnt(2);
4261         out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
4262         if (pfomask & (1 << PFO_C)) {
4263           if (po->operand[1].type == OPT_CONST) {
4264             l = lmod_bytes(po, po->operand[0].lmod) * 8;
4265             j = po->operand[1].val;
4266             j %= l;
4267             if (j != 0) {
4268               if (po->op == OP_SHL)
4269                 j = l - j;
4270               else
4271                 j -= 1;
4272               fprintf(fout, "  cond_c = (%s >> %d) & 1;\n",
4273                 buf1, j);
4274             }
4275             else
4276               ferr(po, "zero shift?\n");
4277           }
4278           else
4279             ferr(po, "TODO\n");
4280           pfomask &= ~(1 << PFO_C);
4281         }
4282         fprintf(fout, "  %s %s= %s;", buf1, op_to_c(po),
4283             out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
4284         output_std_flags(fout, po, &pfomask, buf1);
4285         last_arith_dst = &po->operand[0];
4286         delayed_flag_op = NULL;
4287         break;
4288
4289       case OP_SAR:
4290         assert_operand_cnt(2);
4291         out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
4292         fprintf(fout, "  %s = %s%s >> %s;", buf1,
4293           lmod_cast_s(po, po->operand[0].lmod), buf1,
4294           out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
4295         output_std_flags(fout, po, &pfomask, buf1);
4296         last_arith_dst = &po->operand[0];
4297         delayed_flag_op = NULL;
4298         break;
4299
4300       case OP_ROL:
4301       case OP_ROR:
4302         assert_operand_cnt(2);
4303         out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
4304         if (po->operand[1].type == OPT_CONST) {
4305           j = po->operand[1].val;
4306           j %= lmod_bytes(po, po->operand[0].lmod) * 8;
4307           fprintf(fout, po->op == OP_ROL ?
4308             "  %s = (%s << %d) | (%s >> %d);" :
4309             "  %s = (%s >> %d) | (%s << %d);",
4310             buf1, buf1, j, buf1,
4311             lmod_bytes(po, po->operand[0].lmod) * 8 - j);
4312         }
4313         else
4314           ferr(po, "TODO\n");
4315         output_std_flags(fout, po, &pfomask, buf1);
4316         last_arith_dst = &po->operand[0];
4317         delayed_flag_op = NULL;
4318         break;
4319
4320       case OP_RCL:
4321       case OP_RCR:
4322         assert_operand_cnt(2);
4323         out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
4324         l = lmod_bytes(po, po->operand[0].lmod) * 8;
4325         if (po->operand[1].type == OPT_CONST) {
4326           j = po->operand[1].val % l;
4327           if (j == 0)
4328             ferr(po, "zero rotate\n");
4329           fprintf(fout, "  tmp = (%s >> %d) & 1;\n",
4330             buf1, (po->op == OP_RCL) ? (l - j) : (j - 1));
4331           if (po->op == OP_RCL) {
4332             fprintf(fout,
4333               "  %s = (%s << %d) | (cond_c << %d)",
4334               buf1, buf1, j, j - 1);
4335             if (j != 1)
4336               fprintf(fout, " | (%s >> %d)", buf1, l + 1 - j);
4337           }
4338           else {
4339             fprintf(fout,
4340               "  %s = (%s >> %d) | (cond_c << %d)",
4341               buf1, buf1, j, l - j);
4342             if (j != 1)
4343               fprintf(fout, " | (%s << %d)", buf1, l + 1 - j);
4344           }
4345           fprintf(fout, ";\n");
4346           fprintf(fout, "  cond_c = tmp;");
4347         }
4348         else
4349           ferr(po, "TODO\n");
4350         strcpy(g_comment, (po->op == OP_RCL) ? "rcl" : "rcr");
4351         output_std_flags(fout, po, &pfomask, buf1);
4352         last_arith_dst = &po->operand[0];
4353         delayed_flag_op = NULL;
4354         break;
4355
4356       case OP_XOR:
4357         assert_operand_cnt(2);
4358         propagate_lmod(po, &po->operand[0], &po->operand[1]);
4359         if (IS(opr_name(po, 0), opr_name(po, 1))) {
4360           // special case for XOR
4361           if (pfomask & (1 << PFO_BE)) { // weird, but it happens..
4362             fprintf(fout, "  cond_be = 1;\n");
4363             pfomask &= ~(1 << PFO_BE);
4364           }
4365           fprintf(fout, "  %s = 0;",
4366             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
4367           last_arith_dst = &po->operand[0];
4368           delayed_flag_op = NULL;
4369           break;
4370         }
4371         goto dualop_arith;
4372
4373       case OP_ADD:
4374         assert_operand_cnt(2);
4375         propagate_lmod(po, &po->operand[0], &po->operand[1]);
4376         if (pfomask & (1 << PFO_C)) {
4377           out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
4378           out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
4379           if (po->operand[0].lmod == OPLM_DWORD) {
4380             fprintf(fout, "  tmp64 = (u64)%s + %s;\n", buf1, buf2);
4381             fprintf(fout, "  cond_c = tmp64 >> 32;\n");
4382             fprintf(fout, "  %s = (u32)tmp64;",
4383               out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
4384             strcat(g_comment, "add64");
4385           }
4386           else {
4387             fprintf(fout, "  cond_c = ((u32)%s + %s) >> %d;\n",
4388               buf1, buf2, lmod_bytes(po, po->operand[0].lmod) * 8);
4389             fprintf(fout, "  %s += %s;",
4390               out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
4391               buf2);
4392           }
4393           pfomask &= ~(1 << PFO_C);
4394           output_std_flags(fout, po, &pfomask, buf1);
4395           last_arith_dst = &po->operand[0];
4396           delayed_flag_op = NULL;
4397           break;
4398         }
4399         goto dualop_arith;
4400
4401       case OP_SUB:
4402         assert_operand_cnt(2);
4403         propagate_lmod(po, &po->operand[0], &po->operand[1]);
4404         if (pfomask & (1 << PFO_C)) {
4405           fprintf(fout, "  cond_c = %s < %s;\n",
4406             out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]),
4407             out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
4408           pfomask &= ~(1 << PFO_C);
4409         }
4410         goto dualop_arith;
4411
4412       case OP_ADC:
4413       case OP_SBB:
4414         assert_operand_cnt(2);
4415         propagate_lmod(po, &po->operand[0], &po->operand[1]);
4416         out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
4417         if (po->op == OP_SBB
4418           && IS(po->operand[0].name, po->operand[1].name))
4419         {
4420           // avoid use of unitialized var
4421           fprintf(fout, "  %s = -cond_c;", buf1);
4422           // carry remains what it was
4423           pfomask &= ~(1 << PFO_C);
4424         }
4425         else {
4426           fprintf(fout, "  %s %s= %s + cond_c;", buf1, op_to_c(po),
4427             out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
4428         }
4429         output_std_flags(fout, po, &pfomask, buf1);
4430         last_arith_dst = &po->operand[0];
4431         delayed_flag_op = NULL;
4432         break;
4433
4434       case OP_BSF:
4435         assert_operand_cnt(2);
4436         out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
4437         fprintf(fout, "  %s = %s ? __builtin_ffs(%s) - 1 : 0;",
4438           out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
4439           buf2, buf2);
4440         output_std_flags(fout, po, &pfomask, buf1);
4441         last_arith_dst = &po->operand[0];
4442         delayed_flag_op = NULL;
4443         strcat(g_comment, "bsf");
4444         break;
4445
4446       case OP_INC:
4447       case OP_DEC:
4448         out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
4449         if (po->operand[0].type == OPT_REG) {
4450           strcpy(buf2, po->op == OP_INC ? "++" : "--");
4451           fprintf(fout, "  %s%s;", buf1, buf2);
4452         }
4453         else {
4454           strcpy(buf2, po->op == OP_INC ? "+" : "-");
4455           fprintf(fout, "  %s %s= 1;", buf1, buf2);
4456         }
4457         output_std_flags(fout, po, &pfomask, buf1);
4458         last_arith_dst = &po->operand[0];
4459         delayed_flag_op = NULL;
4460         break;
4461
4462       case OP_NEG:
4463         out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
4464         out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[0]);
4465         fprintf(fout, "  %s = -%s%s;", buf1,
4466           lmod_cast_s(po, po->operand[0].lmod), buf2);
4467         last_arith_dst = &po->operand[0];
4468         delayed_flag_op = NULL;
4469         if (pfomask & (1 << PFO_C)) {
4470           fprintf(fout, "\n  cond_c = (%s != 0);", buf1);
4471           pfomask &= ~(1 << PFO_C);
4472         }
4473         break;
4474
4475       case OP_IMUL:
4476         if (po->operand_cnt == 2) {
4477           propagate_lmod(po, &po->operand[0], &po->operand[1]);
4478           goto dualop_arith;
4479         }
4480         if (po->operand_cnt == 3)
4481           ferr(po, "TODO imul3\n");
4482         // fallthrough
4483       case OP_MUL:
4484         assert_operand_cnt(1);
4485         switch (po->operand[0].lmod) {
4486         case OPLM_DWORD:
4487           strcpy(buf1, po->op == OP_IMUL ? "(s64)(s32)" : "(u64)");
4488           fprintf(fout, "  tmp64 = %seax * %s%s;\n", buf1, buf1,
4489             out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[0]));
4490           fprintf(fout, "  edx = tmp64 >> 32;\n");
4491           fprintf(fout, "  eax = tmp64;");
4492           break;
4493         case OPLM_BYTE:
4494           strcpy(buf1, po->op == OP_IMUL ? "(s16)(s8)" : "(u16)(u8)");
4495           fprintf(fout, "  LOWORD(eax) = %seax * %s;", buf1,
4496             out_src_opr(buf2, sizeof(buf2), po, &po->operand[0],
4497               buf1, 0));
4498           break;
4499         default:
4500           ferr(po, "TODO: unhandled mul type\n");
4501           break;
4502         }
4503         last_arith_dst = NULL;
4504         delayed_flag_op = NULL;
4505         break;
4506
4507       case OP_DIV:
4508       case OP_IDIV:
4509         assert_operand_cnt(1);
4510         if (po->operand[0].lmod != OPLM_DWORD)
4511           ferr(po, "unhandled lmod %d\n", po->operand[0].lmod);
4512
4513         out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
4514         strcpy(buf2, lmod_cast(po, po->operand[0].lmod,
4515           po->op == OP_IDIV));
4516         switch (po->operand[0].lmod) {
4517         case OPLM_DWORD:
4518           if (po->flags & OPF_32BIT)
4519             snprintf(buf3, sizeof(buf3), "%seax", buf2);
4520           else {
4521             fprintf(fout, "  tmp64 = ((u64)edx << 32) | eax;\n");
4522             snprintf(buf3, sizeof(buf3), "%stmp64",
4523               (po->op == OP_IDIV) ? "(s64)" : "");
4524           }
4525           if (po->operand[0].type == OPT_REG
4526             && po->operand[0].reg == xDX)
4527           {
4528             fprintf(fout, "  eax = %s / %s%s;", buf3, buf2, buf1);
4529             fprintf(fout, "  edx = %s %% %s%s;\n", buf3, buf2, buf1);
4530           }
4531           else {
4532             fprintf(fout, "  edx = %s %% %s%s;\n", buf3, buf2, buf1);
4533             fprintf(fout, "  eax = %s / %s%s;", buf3, buf2, buf1);
4534           }
4535           break;
4536         default:
4537           ferr(po, "unhandled division type\n");
4538         }
4539         last_arith_dst = NULL;
4540         delayed_flag_op = NULL;
4541         break;
4542
4543       case OP_TEST:
4544       case OP_CMP:
4545         propagate_lmod(po, &po->operand[0], &po->operand[1]);
4546         if (pfomask != 0) {
4547           for (j = 0; j < 8; j++) {
4548             if (pfomask & (1 << j)) {
4549               out_cmp_test(buf1, sizeof(buf1), po, j, 0);
4550               fprintf(fout, "  cond_%s = %s;",
4551                 parsed_flag_op_names[j], buf1);
4552             }
4553           }
4554           pfomask = 0;
4555         }
4556         else
4557           no_output = 1;
4558         last_arith_dst = NULL;
4559         delayed_flag_op = po;
4560         break;
4561
4562       case OP_SCC:
4563         // SETcc - should already be handled
4564         break;
4565
4566       // note: we reuse OP_Jcc for SETcc, only flags differ
4567       case OP_JCC:
4568         fprintf(fout, "\n    goto %s;", po->operand[0].name);
4569         break;
4570
4571       case OP_JECXZ:
4572         fprintf(fout, "  if (ecx == 0)\n");
4573         fprintf(fout, "    goto %s;", po->operand[0].name);
4574         strcat(g_comment, "jecxz");
4575         break;
4576
4577       case OP_JMP:
4578         assert_operand_cnt(1);
4579         last_arith_dst = NULL;
4580         delayed_flag_op = NULL;
4581
4582         if (po->operand[0].type == OPT_REGMEM) {
4583           ret = sscanf(po->operand[0].name, "%[^[][%[^*]*4]",
4584                   buf1, buf2);
4585           if (ret != 2)
4586             ferr(po, "parse failure for jmp '%s'\n",
4587               po->operand[0].name);
4588           fprintf(fout, "  goto *jt_%s[%s];", buf1, buf2);
4589           break;
4590         }
4591         else if (po->operand[0].type != OPT_LABEL)
4592           ferr(po, "unhandled jmp type\n");
4593
4594         fprintf(fout, "  goto %s;", po->operand[0].name);
4595         break;
4596
4597       case OP_CALL:
4598         assert_operand_cnt(1);
4599         pp = po->pp;
4600         my_assert_not(pp, NULL);
4601
4602         strcpy(buf3, "  ");
4603         if (po->flags & OPF_CC) {
4604           // we treat conditional branch to another func
4605           // (yes such code exists..) as conditional tailcall
4606           strcat(buf3, "  ");
4607           fprintf(fout, " {\n");
4608         }
4609
4610         if (pp->is_fptr && !pp->is_arg) {
4611           fprintf(fout, "%s%s = %s;\n", buf3, pp->name,
4612             out_src_opr(buf1, sizeof(buf1), po, &po->operand[0],
4613               "(void *)", 0));
4614           if (pp->is_unresolved)
4615             fprintf(fout, "%sunresolved_call(\"%s:%d\", %s);\n",
4616               buf3, asmfn, po->asmln, pp->name);
4617         }
4618
4619         fprintf(fout, "%s", buf3);
4620         if (strstr(pp->ret_type.name, "int64")) {
4621           if (po->flags & OPF_TAIL)
4622             ferr(po, "int64 and tail?\n");
4623           fprintf(fout, "tmp64 = ");
4624         }
4625         else if (!IS(pp->ret_type.name, "void")) {
4626           if (po->flags & OPF_TAIL) {
4627             if (!IS(g_func_pp->ret_type.name, "void")) {
4628               fprintf(fout, "return ");
4629               if (g_func_pp->ret_type.is_ptr != pp->ret_type.is_ptr)
4630                 fprintf(fout, "(%s)", g_func_pp->ret_type.name);
4631             }
4632           }
4633           else if (regmask & (1 << xAX)) {
4634             fprintf(fout, "eax = ");
4635             if (pp->ret_type.is_ptr)
4636               fprintf(fout, "(u32)");
4637           }
4638         }
4639
4640         if (pp->name[0] == 0)
4641           ferr(po, "missing pp->name\n");
4642         fprintf(fout, "%s%s(", pp->name,
4643           pp->has_structarg ? "_sa" : "");
4644
4645         if (po->flags & OPF_ATAIL) {
4646           if (pp->argc_stack != g_func_pp->argc_stack
4647             || (pp->argc_stack > 0
4648                 && pp->is_stdcall != g_func_pp->is_stdcall))
4649             ferr(po, "incompatible tailcall\n");
4650           if (g_func_pp->has_retreg)
4651             ferr(po, "TODO: retreg+tailcall\n");
4652
4653           for (arg = j = 0; arg < pp->argc; arg++) {
4654             if (arg > 0)
4655               fprintf(fout, ", ");
4656
4657             cast[0] = 0;
4658             if (pp->arg[arg].type.is_ptr)
4659               snprintf(cast, sizeof(cast), "(%s)",
4660                 pp->arg[arg].type.name);
4661
4662             if (pp->arg[arg].reg != NULL) {
4663               fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
4664               continue;
4665             }
4666             // stack arg
4667             for (; j < g_func_pp->argc; j++)
4668               if (g_func_pp->arg[j].reg == NULL)
4669                 break;
4670             fprintf(fout, "%sa%d", cast, j + 1);
4671             j++;
4672           }
4673         }
4674         else {
4675           for (arg = 0; arg < pp->argc; arg++) {
4676             if (arg > 0)
4677               fprintf(fout, ", ");
4678
4679             cast[0] = 0;
4680             if (pp->arg[arg].type.is_ptr)
4681               snprintf(cast, sizeof(cast), "(%s)",
4682                 pp->arg[arg].type.name);
4683
4684             if (pp->arg[arg].reg != NULL) {
4685               if (pp->arg[arg].type.is_retreg)
4686                 fprintf(fout, "&%s", pp->arg[arg].reg);
4687               else
4688                 fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
4689               continue;
4690             }
4691
4692             // stack arg
4693             tmp_op = pp->arg[arg].datap;
4694             if (tmp_op == NULL)
4695               ferr(po, "parsed_op missing for arg%d\n", arg);
4696
4697             if (tmp_op->flags & OPF_VAPUSH) {
4698               fprintf(fout, "ap");
4699             }
4700             else if (tmp_op->p_argpass != 0) {
4701               fprintf(fout, "a%d", tmp_op->p_argpass);
4702             }
4703             else if (tmp_op->p_argnum != 0) {
4704               fprintf(fout, "%ss_a%d", cast, tmp_op->p_argnum);
4705             }
4706             else {
4707               fprintf(fout, "%s",
4708                 out_src_opr(buf1, sizeof(buf1),
4709                   tmp_op, &tmp_op->operand[0], cast, 0));
4710             }
4711           }
4712         }
4713         fprintf(fout, ");");
4714
4715         if (strstr(pp->ret_type.name, "int64")) {
4716           fprintf(fout, "\n");
4717           fprintf(fout, "%sedx = tmp64 >> 32;\n", buf3);
4718           fprintf(fout, "%seax = tmp64;", buf3);
4719         }
4720
4721         if (pp->is_unresolved) {
4722           snprintf(buf2, sizeof(buf2), " unresolved %dreg",
4723             pp->argc_reg);
4724           strcat(g_comment, buf2);
4725         }
4726
4727         if (po->flags & OPF_TAIL) {
4728           ret = 0;
4729           if (i == opcnt - 1 || pp->is_noreturn)
4730             ret = 0;
4731           else if (IS(pp->ret_type.name, "void"))
4732             ret = 1;
4733           else if (IS(g_func_pp->ret_type.name, "void"))
4734             ret = 1;
4735           // else already handled as 'return f()'
4736
4737           if (ret) {
4738             if (!IS(g_func_pp->ret_type.name, "void")) {
4739               ferr(po, "int func -> void func tailcall?\n");
4740             }
4741             else {
4742               fprintf(fout, "\n%sreturn;", buf3);
4743               strcat(g_comment, " ^ tailcall");
4744             }
4745           }
4746           else
4747             strcat(g_comment, " tailcall");
4748         }
4749         if (pp->is_noreturn)
4750           strcat(g_comment, " noreturn");
4751         if ((po->flags & OPF_ATAIL) && pp->argc_stack > 0)
4752           strcat(g_comment, " argframe");
4753         if (po->flags & OPF_CC)
4754           strcat(g_comment, " cond");
4755
4756         if (po->flags & OPF_CC)
4757           fprintf(fout, "\n  }");
4758
4759         delayed_flag_op = NULL;
4760         last_arith_dst = NULL;
4761         break;
4762
4763       case OP_RET:
4764         if (g_func_pp->is_vararg)
4765           fprintf(fout, "  va_end(ap);\n");
4766         if (g_func_pp->has_retreg) {
4767           for (arg = 0; arg < g_func_pp->argc; arg++)
4768             if (g_func_pp->arg[arg].type.is_retreg)
4769               fprintf(fout, "  *r_%s = %s;\n",
4770                 g_func_pp->arg[arg].reg, g_func_pp->arg[arg].reg);
4771         }
4772  
4773         if (IS(g_func_pp->ret_type.name, "void")) {
4774           if (i != opcnt - 1 || label_pending)
4775             fprintf(fout, "  return;");
4776         }
4777         else if (g_func_pp->ret_type.is_ptr) {
4778           fprintf(fout, "  return (%s)eax;",
4779             g_func_pp->ret_type.name);
4780         }
4781         else if (IS(g_func_pp->ret_type.name, "__int64"))
4782           fprintf(fout, "  return ((u64)edx << 32) | eax;");
4783         else
4784           fprintf(fout, "  return eax;");
4785
4786         last_arith_dst = NULL;
4787         delayed_flag_op = NULL;
4788         break;
4789
4790       case OP_PUSH:
4791         out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
4792         if (po->p_argnum != 0) {
4793           // special case - saved func arg
4794           fprintf(fout, "  s_a%d = %s;", po->p_argnum, buf1);
4795           break;
4796         }
4797         else if (po->flags & OPF_RSAVE) {
4798           fprintf(fout, "  s_%s = %s;", buf1, buf1);
4799           break;
4800         }
4801         else if (g_func_pp->is_userstack) {
4802           fprintf(fout, "  *(--esp) = %s;", buf1);
4803           break;
4804         }
4805         if (!(g_ida_func_attr & IDAFA_NORETURN))
4806           ferr(po, "stray push encountered\n");
4807         no_output = 1;
4808         break;
4809
4810       case OP_POP:
4811         if (po->flags & OPF_RSAVE) {
4812           out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
4813           fprintf(fout, "  %s = s_%s;", buf1, buf1);
4814           break;
4815         }
4816         else if (po->datap != NULL) {
4817           // push/pop pair
4818           tmp_op = po->datap;
4819           out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
4820           fprintf(fout, "  %s = %s;", buf1,
4821             out_src_opr(buf2, sizeof(buf2),
4822               tmp_op, &tmp_op->operand[0],
4823               default_cast_to(buf3, sizeof(buf3), &po->operand[0]), 0));
4824           break;
4825         }
4826         else if (g_func_pp->is_userstack) {
4827           fprintf(fout, "  %s = *esp++;",
4828             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
4829           break;
4830         }
4831         else
4832           ferr(po, "stray pop encountered\n");
4833         break;
4834
4835       case OP_NOP:
4836         no_output = 1;
4837         break;
4838
4839       default:
4840         no_output = 1;
4841         ferr(po, "unhandled op type %d, flags %x\n",
4842           po->op, po->flags);
4843         break;
4844     }
4845
4846     if (g_comment[0] != 0) {
4847       char *p = g_comment;
4848       while (my_isblank(*p))
4849         p++;
4850       fprintf(fout, "  // %s", p);
4851       g_comment[0] = 0;
4852       no_output = 0;
4853     }
4854     if (!no_output)
4855       fprintf(fout, "\n");
4856
4857     // some sanity checking
4858     if (po->flags & OPF_REP) {
4859       if (po->op != OP_STOS && po->op != OP_MOVS
4860           && po->op != OP_CMPS && po->op != OP_SCAS)
4861         ferr(po, "unexpected rep\n");
4862       if (!(po->flags & (OPF_REPZ|OPF_REPNZ))
4863           && (po->op == OP_CMPS || po->op == OP_SCAS))
4864         ferr(po, "cmps/scas with plain rep\n");
4865     }
4866     if ((po->flags & (OPF_REPZ|OPF_REPNZ))
4867         && po->op != OP_CMPS && po->op != OP_SCAS)
4868       ferr(po, "unexpected repz/repnz\n");
4869
4870     if (pfomask != 0)
4871       ferr(po, "missed flag calc, pfomask=%x\n", pfomask);
4872
4873     // see is delayed flag stuff is still valid
4874     if (delayed_flag_op != NULL && delayed_flag_op != po) {
4875       if (is_any_opr_modified(delayed_flag_op, po, 0))
4876         delayed_flag_op = NULL;
4877     }
4878
4879     if (last_arith_dst != NULL && last_arith_dst != &po->operand[0]) {
4880       if (is_opr_modified(last_arith_dst, po))
4881         last_arith_dst = NULL;
4882     }
4883
4884     label_pending = 0;
4885   }
4886
4887   if (g_stack_fsz && !g_stack_frame_used)
4888     fprintf(fout, "  (void)sf;\n");
4889
4890   fprintf(fout, "}\n\n");
4891
4892   // cleanup
4893   for (i = 0; i < opcnt; i++) {
4894     struct label_ref *lr, *lr_del;
4895
4896     lr = g_label_refs[i].next;
4897     while (lr != NULL) {
4898       lr_del = lr;
4899       lr = lr->next;
4900       free(lr_del);
4901     }
4902     g_label_refs[i].i = -1;
4903     g_label_refs[i].next = NULL;
4904
4905     if (ops[i].op == OP_CALL) {
4906       if (ops[i].pp)
4907         proto_release(ops[i].pp);
4908     }
4909   }
4910   g_func_pp = NULL;
4911 }
4912
4913 static void set_label(int i, const char *name)
4914 {
4915   const char *p;
4916   int len;
4917
4918   len = strlen(name);
4919   p = strchr(name, ':');
4920   if (p != NULL)
4921     len = p - name;
4922
4923   if (len > sizeof(g_labels[0]) - 1)
4924     aerr("label '%s' too long: %d\n", name, len);
4925   if (g_labels[i][0] != 0 && !IS_START(g_labels[i], "algn_"))
4926     aerr("dupe label '%s' vs '%s'?\n", name, g_labels[i]);
4927   memcpy(g_labels[i], name, len);
4928   g_labels[i][len] = 0;
4929 }
4930
4931 // '=' needs special treatment..
4932 static char *next_word_s(char *w, size_t wsize, char *s)
4933 {
4934         size_t i;
4935
4936         s = sskip(s);
4937
4938         for (i = 0; i < wsize - 1; i++) {
4939                 if (s[i] == 0 || my_isblank(s[i]) || (s[i] == '=' && i > 0))
4940                         break;
4941                 w[i] = s[i];
4942         }
4943         w[i] = 0;
4944
4945         if (s[i] != 0 && !my_isblank(s[i]) && s[i] != '=')
4946                 printf("warning: '%s' truncated\n", w);
4947
4948         return s + i;
4949 }
4950
4951 struct chunk_item {
4952   char *name;
4953   long fptr;
4954   int asmln;
4955 };
4956
4957 static struct chunk_item *func_chunks;
4958 static int func_chunk_cnt;
4959 static int func_chunk_alloc;
4960
4961 static void add_func_chunk(FILE *fasm, const char *name, int line)
4962 {
4963   if (func_chunk_cnt >= func_chunk_alloc) {
4964     func_chunk_alloc *= 2;
4965     func_chunks = realloc(func_chunks,
4966       func_chunk_alloc * sizeof(func_chunks[0]));
4967     my_assert_not(func_chunks, NULL);
4968   }
4969   func_chunks[func_chunk_cnt].fptr = ftell(fasm);
4970   func_chunks[func_chunk_cnt].name = strdup(name);
4971   func_chunks[func_chunk_cnt].asmln = line;
4972   func_chunk_cnt++;
4973 }
4974
4975 static int cmp_chunks(const void *p1, const void *p2)
4976 {
4977   const struct chunk_item *c1 = p1, *c2 = p2;
4978   return strcmp(c1->name, c2->name);
4979 }
4980
4981 static int cmpstringp(const void *p1, const void *p2)
4982 {
4983   return strcmp(*(char * const *)p1, *(char * const *)p2);
4984 }
4985
4986 static void scan_ahead(FILE *fasm)
4987 {
4988   char words[2][256];
4989   char line[256];
4990   long oldpos;
4991   int oldasmln;
4992   int wordc;
4993   char *p;
4994   int i;
4995
4996   oldpos = ftell(fasm);
4997   oldasmln = asmln;
4998
4999   while (fgets(line, sizeof(line), fasm))
5000   {
5001     wordc = 0;
5002     asmln++;
5003
5004     p = sskip(line);
5005     if (*p == 0)
5006       continue;
5007
5008     if (*p == ';')
5009     {
5010       // get rid of random tabs
5011       for (i = 0; line[i] != 0; i++)
5012         if (line[i] == '\t')
5013           line[i] = ' ';
5014
5015       if (p[2] == 'S' && IS_START(p, "; START OF FUNCTION CHUNK FOR "))
5016       {
5017         p += 30;
5018         next_word(words[0], sizeof(words[0]), p);
5019         if (words[0][0] == 0)
5020           aerr("missing name for func chunk?\n");
5021
5022         add_func_chunk(fasm, words[0], asmln);
5023       }
5024       else if (IS_START(p, "; sctend"))
5025         break;
5026
5027       continue;
5028     } // *p == ';'
5029
5030     for (wordc = 0; wordc < ARRAY_SIZE(words); wordc++) {
5031       words[wordc][0] = 0;
5032       p = sskip(next_word_s(words[wordc], sizeof(words[0]), p));
5033       if (*p == 0 || *p == ';') {
5034         wordc++;
5035         break;
5036       }
5037     }
5038
5039     if (wordc == 2 && IS(words[1], "ends"))
5040       break;
5041   }
5042
5043   fseek(fasm, oldpos, SEEK_SET);
5044   asmln = oldasmln;
5045 }
5046
5047 int main(int argc, char *argv[])
5048 {
5049   FILE *fout, *fasm, *frlist;
5050   struct parsed_data *pd = NULL;
5051   int pd_alloc = 0;
5052   char **rlist = NULL;
5053   int rlist_len = 0;
5054   int rlist_alloc = 0;
5055   int func_chunks_used = 0;
5056   int func_chunks_sorted = 0;
5057   int func_chunk_i = -1;
5058   long func_chunk_ret = 0;
5059   int func_chunk_ret_ln = 0;
5060   int scanned_ahead = 0;
5061   char line[256];
5062   char words[20][256];
5063   enum opr_lenmod lmod;
5064   char *sctproto = NULL;
5065   int in_func = 0;
5066   int pending_endp = 0;
5067   int skip_func = 0;
5068   int skip_warned = 0;
5069   int eq_alloc;
5070   int verbose = 0;
5071   int multi_seg = 0;
5072   int end = 0;
5073   int arg_out;
5074   int arg;
5075   int pi = 0;
5076   int i, j;
5077   int ret, len;
5078   char *p;
5079   int wordc;
5080
5081   for (arg = 1; arg < argc; arg++) {
5082     if (IS(argv[arg], "-v"))
5083       verbose = 1;
5084     else if (IS(argv[arg], "-rf"))
5085       g_allow_regfunc = 1;
5086     else if (IS(argv[arg], "-m"))
5087       multi_seg = 1;
5088     else
5089       break;
5090   }
5091
5092   if (argc < arg + 3) {
5093     printf("usage:\n%s [-v] [-rf] [-m] <.c> <.asm> <hdrf> [rlist]*\n",
5094       argv[0]);
5095     return 1;
5096   }
5097
5098   arg_out = arg++;
5099
5100   asmfn = argv[arg++];
5101   fasm = fopen(asmfn, "r");
5102   my_assert_not(fasm, NULL);
5103
5104   hdrfn = argv[arg++];
5105   g_fhdr = fopen(hdrfn, "r");
5106   my_assert_not(g_fhdr, NULL);
5107
5108   rlist_alloc = 64;
5109   rlist = malloc(rlist_alloc * sizeof(rlist[0]));
5110   my_assert_not(rlist, NULL);
5111   // needs special handling..
5112   rlist[rlist_len++] = "__alloca_probe";
5113
5114   func_chunk_alloc = 32;
5115   func_chunks = malloc(func_chunk_alloc * sizeof(func_chunks[0]));
5116   my_assert_not(func_chunks, NULL);
5117
5118   memset(words, 0, sizeof(words));
5119
5120   for (; arg < argc; arg++) {
5121     frlist = fopen(argv[arg], "r");
5122     my_assert_not(frlist, NULL);
5123
5124     while (fgets(line, sizeof(line), frlist)) {
5125       p = sskip(line);
5126       if (*p == 0 || *p == ';')
5127         continue;
5128       if (*p == '#') {
5129         if (IS_START(p, "#if 0")
5130          || (g_allow_regfunc && IS_START(p, "#if NO_REGFUNC")))
5131         {
5132           skip_func = 1;
5133         }
5134         else if (IS_START(p, "#endif"))
5135           skip_func = 0;
5136         continue;
5137       }
5138       if (skip_func)
5139         continue;
5140
5141       p = next_word(words[0], sizeof(words[0]), p);
5142       if (words[0][0] == 0)
5143         continue;
5144
5145       if (rlist_len >= rlist_alloc) {
5146         rlist_alloc = rlist_alloc * 2 + 64;
5147         rlist = realloc(rlist, rlist_alloc * sizeof(rlist[0]));
5148         my_assert_not(rlist, NULL);
5149       }
5150       rlist[rlist_len++] = strdup(words[0]);
5151     }
5152     skip_func = 0;
5153
5154     fclose(frlist);
5155     frlist = NULL;
5156   }
5157
5158   if (rlist_len > 0)
5159     qsort(rlist, rlist_len, sizeof(rlist[0]), cmpstringp);
5160
5161   fout = fopen(argv[arg_out], "w");
5162   my_assert_not(fout, NULL);
5163
5164   eq_alloc = 128;
5165   g_eqs = malloc(eq_alloc * sizeof(g_eqs[0]));
5166   my_assert_not(g_eqs, NULL);
5167
5168   for (i = 0; i < ARRAY_SIZE(g_label_refs); i++) {
5169     g_label_refs[i].i = -1;
5170     g_label_refs[i].next = NULL;
5171   }
5172
5173   while (fgets(line, sizeof(line), fasm))
5174   {
5175     wordc = 0;
5176     asmln++;
5177
5178     p = sskip(line);
5179     if (*p == 0)
5180       continue;
5181
5182     // get rid of random tabs
5183     for (i = 0; line[i] != 0; i++)
5184       if (line[i] == '\t')
5185         line[i] = ' ';
5186
5187     if (*p == ';')
5188     {
5189       if (p[2] == '=' && IS_START(p, "; =============== S U B"))
5190         goto do_pending_endp; // eww..
5191
5192       if (p[2] == 'A' && IS_START(p, "; Attributes:"))
5193       {
5194         static const char *attrs[] = {
5195           "bp-based frame",
5196           "library function",
5197           "static",
5198           "noreturn",
5199           "thunk",
5200           "fpd=",
5201         };
5202
5203         // parse IDA's attribute-list comment
5204         g_ida_func_attr = 0;
5205         p = sskip(p + 13);
5206
5207         for (; *p != 0; p = sskip(p)) {
5208           for (i = 0; i < ARRAY_SIZE(attrs); i++) {
5209             if (!strncmp(p, attrs[i], strlen(attrs[i]))) {
5210               g_ida_func_attr |= 1 << i;
5211               p += strlen(attrs[i]);
5212               break;
5213             }
5214           }
5215           if (i == ARRAY_SIZE(attrs)) {
5216             anote("unparsed IDA attr: %s\n", p);
5217             break;
5218           }
5219           if (IS(attrs[i], "fpd=")) {
5220             p = next_word(words[0], sizeof(words[0]), p);
5221             // ignore for now..
5222           }
5223         }
5224       }
5225       else if (p[2] == 'S' && IS_START(p, "; START OF FUNCTION CHUNK FOR "))
5226       {
5227         p += 30;
5228         next_word(words[0], sizeof(words[0]), p);
5229         if (words[0][0] == 0)
5230           aerr("missing name for func chunk?\n");
5231
5232         if (!scanned_ahead) {
5233           add_func_chunk(fasm, words[0], asmln);
5234           func_chunks_sorted = 0;
5235         }
5236       }
5237       else if (p[2] == 'E' && IS_START(p, "; END OF FUNCTION CHUNK"))
5238       {
5239         if (func_chunk_i >= 0) {
5240           if (func_chunk_i < func_chunk_cnt
5241             && IS(func_chunks[func_chunk_i].name, g_func))
5242           {
5243             // move on to next chunk
5244             ret = fseek(fasm, func_chunks[func_chunk_i].fptr, SEEK_SET);
5245             if (ret)
5246               aerr("seek failed for '%s' chunk #%d\n",
5247                 g_func, func_chunk_i);
5248             asmln = func_chunks[func_chunk_i].asmln;
5249             func_chunk_i++;
5250           }
5251           else {
5252             if (func_chunk_ret == 0)
5253               aerr("no return from chunk?\n");
5254             fseek(fasm, func_chunk_ret, SEEK_SET);
5255             asmln = func_chunk_ret_ln;
5256             func_chunk_ret = 0;
5257             pending_endp = 1;
5258           }
5259         }
5260       }
5261       else if (p[2] == 'F' && IS_START(p, "; FUNCTION CHUNK AT ")) {
5262         func_chunks_used = 1;
5263         p += 20;
5264         if (IS_START(g_func, "sub_")) {
5265           unsigned long addr = strtoul(p, NULL, 16);
5266           unsigned long f_addr = strtoul(g_func + 4, NULL, 16);
5267           if (addr > f_addr && !scanned_ahead) {
5268             anote("scan_ahead caused by '%s', addr %lx\n",
5269               g_func, addr);
5270             scan_ahead(fasm);
5271             scanned_ahead = 1;
5272             func_chunks_sorted = 0;
5273           }
5274         }
5275       }
5276       continue;
5277     } // *p == ';'
5278
5279 parse_words:
5280     for (i = wordc; i < ARRAY_SIZE(words); i++)
5281       words[i][0] = 0;
5282     for (wordc = 0; wordc < ARRAY_SIZE(words); wordc++) {
5283       p = sskip(next_word_s(words[wordc], sizeof(words[0]), p));
5284       if (*p == 0 || *p == ';') {
5285         wordc++;
5286         break;
5287       }
5288     }
5289     if (*p != 0 && *p != ';')
5290       aerr("too many words\n");
5291
5292     // alow asm patches in comments
5293     if (*p == ';') {
5294       if (IS_START(p, "; sctpatch:")) {
5295         p = sskip(p + 11);
5296         if (*p == 0 || *p == ';')
5297           continue;
5298         goto parse_words; // lame
5299       }
5300       if (IS_START(p, "; sctproto:")) {
5301         sctproto = strdup(p + 11);
5302       }
5303       else if (IS_START(p, "; sctend")) {
5304         end = 1;
5305         if (!pending_endp)
5306           break;
5307       }
5308     }
5309
5310     if (wordc == 0) {
5311       // shouldn't happen
5312       awarn("wordc == 0?\n");
5313       continue;
5314     }
5315
5316     // don't care about this:
5317     if (words[0][0] == '.'
5318         || IS(words[0], "include")
5319         || IS(words[0], "assume") || IS(words[1], "segment")
5320         || IS(words[0], "align"))
5321     {
5322       continue;
5323     }
5324
5325 do_pending_endp:
5326     // do delayed endp processing to collect switch jumptables
5327     if (pending_endp) {
5328       if (in_func && !skip_func && !end && wordc >= 2
5329           && ((words[0][0] == 'd' && words[0][2] == 0)
5330               || (words[1][0] == 'd' && words[1][2] == 0)))
5331       {
5332         i = 1;
5333         if (words[1][0] == 'd' && words[1][2] == 0) {
5334           // label
5335           if (g_func_pd_cnt >= pd_alloc) {
5336             pd_alloc = pd_alloc * 2 + 16;
5337             g_func_pd = realloc(g_func_pd,
5338               sizeof(g_func_pd[0]) * pd_alloc);
5339             my_assert_not(g_func_pd, NULL);
5340           }
5341           pd = &g_func_pd[g_func_pd_cnt];
5342           g_func_pd_cnt++;
5343           memset(pd, 0, sizeof(*pd));
5344           strcpy(pd->label, words[0]);
5345           pd->type = OPT_CONST;
5346           pd->lmod = lmod_from_directive(words[1]);
5347           i = 2;
5348         }
5349         else {
5350           if (pd == NULL) {
5351             if (verbose)
5352               anote("skipping alignment byte?\n");
5353             continue;
5354           }
5355           lmod = lmod_from_directive(words[0]);
5356           if (lmod != pd->lmod)
5357             aerr("lmod change? %d->%d\n", pd->lmod, lmod);
5358         }
5359
5360         if (pd->count_alloc < pd->count + wordc) {
5361           pd->count_alloc = pd->count_alloc * 2 + 14 + wordc;
5362           pd->d = realloc(pd->d, sizeof(pd->d[0]) * pd->count_alloc);
5363           my_assert_not(pd->d, NULL);
5364         }
5365         for (; i < wordc; i++) {
5366           if (IS(words[i], "offset")) {
5367             pd->type = OPT_OFFSET;
5368             i++;
5369           }
5370           p = strchr(words[i], ',');
5371           if (p != NULL)
5372             *p = 0;
5373           if (pd->type == OPT_OFFSET)
5374             pd->d[pd->count].u.label = strdup(words[i]);
5375           else
5376             pd->d[pd->count].u.val = parse_number(words[i]);
5377           pd->d[pd->count].bt_i = -1;
5378           pd->count++;
5379         }
5380         continue;
5381       }
5382
5383       if (in_func && !skip_func)
5384         gen_func(fout, g_fhdr, g_func, pi);
5385
5386       pending_endp = 0;
5387       in_func = 0;
5388       g_ida_func_attr = 0;
5389       skip_warned = 0;
5390       skip_func = 0;
5391       g_func[0] = 0;
5392       func_chunks_used = 0;
5393       func_chunk_i = -1;
5394       if (pi != 0) {
5395         memset(&ops, 0, pi * sizeof(ops[0]));
5396         memset(g_labels, 0, pi * sizeof(g_labels[0]));
5397         pi = 0;
5398       }
5399       g_eqcnt = 0;
5400       for (i = 0; i < g_func_pd_cnt; i++) {
5401         pd = &g_func_pd[i];
5402         if (pd->type == OPT_OFFSET) {
5403           for (j = 0; j < pd->count; j++)
5404             free(pd->d[j].u.label);
5405         }
5406         free(pd->d);
5407         pd->d = NULL;
5408       }
5409       g_func_pd_cnt = 0;
5410       pd = NULL;
5411
5412       if (end)
5413         break;
5414       if (wordc == 0)
5415         continue;
5416     }
5417
5418     if (IS(words[1], "proc")) {
5419       if (in_func)
5420         aerr("proc '%s' while in_func '%s'?\n",
5421           words[0], g_func);
5422       p = words[0];
5423       if (bsearch(&p, rlist, rlist_len, sizeof(rlist[0]), cmpstringp))
5424         skip_func = 1;
5425       strcpy(g_func, words[0]);
5426       set_label(0, words[0]);
5427       in_func = 1;
5428       continue;
5429     }
5430
5431     if (IS(words[1], "endp"))
5432     {
5433       if (!in_func)
5434         aerr("endp '%s' while not in_func?\n", words[0]);
5435       if (!IS(g_func, words[0]))
5436         aerr("endp '%s' while in_func '%s'?\n",
5437           words[0], g_func);
5438
5439       if ((g_ida_func_attr & IDAFA_THUNK) && pi == 1
5440         && ops[0].op == OP_JMP && ops[0].operand[0].had_ds)
5441       {
5442         // import jump
5443         skip_func = 1;
5444       }
5445
5446       if (!skip_func && func_chunks_used) {
5447         // start processing chunks
5448         struct chunk_item *ci, key = { g_func, 0 };
5449
5450         func_chunk_ret = ftell(fasm);
5451         func_chunk_ret_ln = asmln;
5452         if (!func_chunks_sorted) {
5453           qsort(func_chunks, func_chunk_cnt,
5454             sizeof(func_chunks[0]), cmp_chunks);
5455           func_chunks_sorted = 1;
5456         }
5457         ci = bsearch(&key, func_chunks, func_chunk_cnt,
5458                sizeof(func_chunks[0]), cmp_chunks);
5459         if (ci == NULL)
5460           aerr("'%s' needs chunks, but none found\n", g_func);
5461         func_chunk_i = ci - func_chunks;
5462         for (; func_chunk_i > 0; func_chunk_i--)
5463           if (!IS(func_chunks[func_chunk_i - 1].name, g_func))
5464             break;
5465
5466         ret = fseek(fasm, func_chunks[func_chunk_i].fptr, SEEK_SET);
5467         if (ret)
5468           aerr("seek failed for '%s' chunk #%d\n", g_func, func_chunk_i);
5469         asmln = func_chunks[func_chunk_i].asmln;
5470         func_chunk_i++;
5471         continue;
5472       }
5473       pending_endp = 1;
5474       continue;
5475     }
5476
5477     if (wordc == 2 && IS(words[1], "ends")) {
5478       if (!multi_seg) {
5479         end = 1;
5480         if (pending_endp)
5481           goto do_pending_endp;
5482         break;
5483       }
5484
5485       // scan for next text segment
5486       while (fgets(line, sizeof(line), fasm)) {
5487         asmln++;
5488         p = sskip(line);
5489         if (*p == 0 || *p == ';')
5490           continue;
5491
5492         if (strstr(p, "segment para public 'CODE' use32"))
5493           break;
5494       }
5495
5496       continue;
5497     }
5498
5499     p = strchr(words[0], ':');
5500     if (p != NULL) {
5501       set_label(pi, words[0]);
5502       continue;
5503     }
5504
5505     if (!in_func || skip_func) {
5506       if (!skip_warned && !skip_func && g_labels[pi][0] != 0) {
5507         if (verbose)
5508           anote("skipping from '%s'\n", g_labels[pi]);
5509         skip_warned = 1;
5510       }
5511       g_labels[pi][0] = 0;
5512       continue;
5513     }
5514
5515     if (wordc > 1 && IS(words[1], "="))
5516     {
5517       if (wordc != 5)
5518         aerr("unhandled equ, wc=%d\n", wordc);
5519       if (g_eqcnt >= eq_alloc) {
5520         eq_alloc *= 2;
5521         g_eqs = realloc(g_eqs, eq_alloc * sizeof(g_eqs[0]));
5522         my_assert_not(g_eqs, NULL);
5523       }
5524
5525       len = strlen(words[0]);
5526       if (len > sizeof(g_eqs[0].name) - 1)
5527         aerr("equ name too long: %d\n", len);
5528       strcpy(g_eqs[g_eqcnt].name, words[0]);
5529
5530       if (!IS(words[3], "ptr"))
5531         aerr("unhandled equ\n");
5532       if (IS(words[2], "dword"))
5533         g_eqs[g_eqcnt].lmod = OPLM_DWORD;
5534       else if (IS(words[2], "word"))
5535         g_eqs[g_eqcnt].lmod = OPLM_WORD;
5536       else if (IS(words[2], "byte"))
5537         g_eqs[g_eqcnt].lmod = OPLM_BYTE;
5538       else
5539         aerr("bad lmod: '%s'\n", words[2]);
5540
5541       g_eqs[g_eqcnt].offset = parse_number(words[4]);
5542       g_eqcnt++;
5543       continue;
5544     }
5545
5546     if (pi >= ARRAY_SIZE(ops))
5547       aerr("too many ops\n");
5548
5549     parse_op(&ops[pi], words, wordc);
5550
5551     if (sctproto != NULL) {
5552       if (ops[pi].op == OP_CALL || ops[pi].op == OP_JMP)
5553         ops[pi].datap = sctproto;
5554       sctproto = NULL;
5555     }
5556     pi++;
5557   }
5558
5559   fclose(fout);
5560   fclose(fasm);
5561   fclose(g_fhdr);
5562
5563   return 0;
5564 }
5565
5566 // vim:ts=2:shiftwidth=2:expandtab