improved type handling and cast generation
[ia32rtools.git] / tools / translate.c
index e77e318..7da8be5 100644 (file)
@@ -33,8 +33,10 @@ enum op_flags {
   OPF_JMP    = (1 << 3), /* branches, ret and call */
   OPF_CC     = (1 << 4), /* uses flags */
   OPF_TAIL   = (1 << 5), /* ret or tail call */
-  OPF_REP    = (1 << 6), /* prefixed by rep */
-  OPF_RSAVE  = (1 << 7), /* push/pop is local reg save/load */
+  OPF_RSAVE  = (1 << 6), /* push/pop is local reg save/load */
+  OPF_REP    = (1 << 7), /* prefixed by rep */
+  OPF_REPZ   = (1 << 8), /* rep is repe/repz */
+  OPF_REPNZ  = (1 << 9), /* rep is repne/repnz */
 };
 
 enum op_op {
@@ -50,6 +52,7 @@ enum op_op {
        OP_CDQ,
        OP_STOS,
        OP_MOVS,
+       OP_CMPS,
        OP_RET,
        OP_ADD,
        OP_SUB,
@@ -113,7 +116,8 @@ enum opr_lenmod {
 struct parsed_opr {
   enum opr_type type;
   enum opr_lenmod lmod;
-  unsigned int is_ptr:1;  // pointer in C
+  unsigned int is_ptr:1;   // pointer in C
+  unsigned int is_array:1; // array in C
   int reg;
   unsigned int val;
   char name[256];
@@ -153,7 +157,7 @@ struct parsed_data {
   struct {
     union {
       char *label;
-      int val;
+      unsigned int val;
     } u;
     int bt_i;
   } *d;
@@ -190,7 +194,7 @@ static int g_sp_frame;
 static int g_stack_fsz;
 static int g_ida_func_attr;
 #define ferr(op_, fmt, ...) do { \
-  printf("error:%s:#%ld: '%s': " fmt, g_func, (op_) - ops, \
+  printf("error:%s:#%zd: '%s': " fmt, g_func, (op_) - ops, \
     dump_op(op_), ##__VA_ARGS__); \
   fcloseall(); \
   exit(1); \
@@ -232,7 +236,8 @@ static int char_array_i(const char *array[], size_t len, const char *s)
   return -1;
 }
 
-static void printf_number(char *buf, size_t buf_size, long number)
+static void printf_number(char *buf, size_t buf_size,
+  unsigned long number)
 {
   // output in C-friendly form
   snprintf(buf, buf_size, number < 10 ? "%lu" : "0x%02lx", number);
@@ -266,14 +271,14 @@ static int parse_reg(enum opr_lenmod *reg_lmod, const char *s)
   return -1;
 }
 
-static long parse_number(const char *number)
+static unsigned long parse_number(const char *number)
 {
   int len = strlen(number);
   const char *p = number;
   char *endp = NULL;
+  unsigned long ret;
   int neg = 0;
   int bad;
-  long ret;
 
   if (*p == '-') {
     neg = 1;
@@ -282,11 +287,11 @@ static long parse_number(const char *number)
   if (len > 1 && *p == '0')
     p++;
   if (number[len - 1] == 'h') {
-    ret = strtol(p, &endp, 16);
+    ret = strtoul(p, &endp, 16);
     bad = (*endp != 'h');
   }
   else {
-    ret = strtol(p, &endp, 10);
+    ret = strtoul(p, &endp, 10);
     bad = (*endp != 0);
   }
   if (bad)
@@ -452,11 +457,9 @@ static int guess_lmod_from_name(struct parsed_opr *opr)
   return 0;
 }
 
-static int guess_lmod_from_c_type(struct parsed_opr *opr, const char *c_type)
+static int guess_lmod_from_c_type(enum opr_lenmod *lmod,
+  const struct parsed_type *c_type)
 {
-  static const char *ptr_types[] = {
-    "LPCSTR",
-  };
   static const char *dword_types[] = {
     "int", "_DWORD", "DWORD", "HANDLE", "HWND", "HMODULE",
   };
@@ -466,44 +469,37 @@ static int guess_lmod_from_c_type(struct parsed_opr *opr, const char *c_type)
   static const char *byte_types[] = {
     "char", "__int8", "unsigned __int8", "BYTE",
   };
+  const char *n;
   int i;
 
-  if (strchr(c_type, '*')) {
-    opr->lmod = OPLM_DWORD;
-    opr->is_ptr = 1;
+  if (c_type->is_ptr) {
+    *lmod = OPLM_DWORD;
     return 1;
   }
 
-  for (i = 0; i < ARRAY_SIZE(dword_types); i++) {
-    if (IS(c_type, dword_types[i])) {
-      opr->lmod = OPLM_DWORD;
-      return 1;
-    }
-  }
+  n = skip_type_mod(c_type->name);
 
-  for (i = 0; i < ARRAY_SIZE(ptr_types); i++) {
-    if (IS(c_type, ptr_types[i])) {
-      opr->lmod = OPLM_DWORD;
-      opr->is_ptr = 1;
+  for (i = 0; i < ARRAY_SIZE(dword_types); i++) {
+    if (IS(n, dword_types[i])) {
+      *lmod = OPLM_DWORD;
       return 1;
     }
   }
 
   for (i = 0; i < ARRAY_SIZE(word_types); i++) {
-    if (IS(c_type, word_types[i])) {
-      opr->lmod = OPLM_WORD;
+    if (IS(n, word_types[i])) {
+      *lmod = OPLM_WORD;
       return 1;
     }
   }
 
   for (i = 0; i < ARRAY_SIZE(byte_types); i++) {
-    if (IS(c_type, byte_types[i])) {
-      opr->lmod = OPLM_BYTE;
+    if (IS(n, byte_types[i])) {
+      *lmod = OPLM_BYTE;
       return 1;
     }
   }
 
-  anote("unhandled C type '%s' for '%s'\n", c_type, opr->name);
   return 0;
 }
 
@@ -538,8 +534,8 @@ static int parse_operand(struct parsed_opr *opr,
 {
   struct parsed_proto pp;
   enum opr_lenmod tmplmod;
+  unsigned long number;
   int ret, len;
-  long number;
   int wordc_in;
   char *tmp;
   int i;
@@ -673,8 +669,12 @@ static int parse_operand(struct parsed_opr *opr,
       opr->lmod = OPLM_DWORD;
       opr->is_ptr = 1;
     }
-    else if (opr->lmod == OPLM_UNSPEC)
-      guess_lmod_from_c_type(opr, pp.ret_type);
+    else if (opr->lmod == OPLM_UNSPEC) {
+      if (!guess_lmod_from_c_type(&opr->lmod, &pp.type))
+        anote("unhandled C type '%s' for '%s'\n", pp.type.name, opr->name);
+    }
+    opr->is_ptr = pp.type.is_ptr;
+    opr->is_array = pp.type.is_array;
   }
   proto_release(&pp);
 
@@ -688,6 +688,10 @@ static const struct {
   unsigned int flags;
 } pref_table[] = {
   { "rep",    OPF_REP },
+  { "repe",   OPF_REP|OPF_REPZ },
+  { "repz",   OPF_REP|OPF_REPZ },
+  { "repne",  OPF_REP|OPF_REPNZ },
+  { "repnz",  OPF_REP|OPF_REPNZ },
 };
 
 static const struct {
@@ -712,6 +716,9 @@ static const struct {
   { "movsb",OP_MOVS,   0, 0, OPF_DATA },
   { "movsw",OP_MOVS,   0, 0, OPF_DATA },
   { "movsd",OP_MOVS,   0, 0, OPF_DATA },
+  { "cmpsb",OP_CMPS,   0, 0, OPF_DATA|OPF_FLAGS },
+  { "cmpsw",OP_CMPS,   0, 0, OPF_DATA|OPF_FLAGS },
+  { "cmpsd",OP_CMPS,   0, 0, OPF_DATA|OPF_FLAGS },
   { "add",  OP_ADD,    2, 2, OPF_DATA|OPF_FLAGS },
   { "sub",  OP_SUB,    2, 2, OPF_DATA|OPF_FLAGS },
   { "and",  OP_AND,    2, 2, OPF_DATA|OPF_FLAGS },
@@ -885,13 +892,14 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc)
     break;
 
   case OP_MOVS:
+  case OP_CMPS:
     if (op->operand_cnt != 0)
       break;
-    if      (IS(words[op_w], "movsb"))
+    if      (words[op_w][4] == 'b')
       lmod = OPLM_BYTE;
-    else if (IS(words[op_w], "movsw"))
+    else if (words[op_w][4] == 'w')
       lmod = OPLM_WORD;
-    else if (IS(words[op_w], "movsd"))
+    else if (words[op_w][4] == 'd')
       lmod = OPLM_DWORD;
     op->operand_cnt = 3;
     setup_reg_opr(&op->operand[0], xDI, lmod, &op->regmask_src);
@@ -932,6 +940,23 @@ static void parse_op(struct parsed_op *op, char words[16][256], int wordc)
       op->operand[1].lmod = OPLM_BYTE;
     break;
 
+  case OP_PUSH:
+    if (op->operand[0].lmod == OPLM_UNSPEC
+        && (op->operand[0].type == OPT_CONST
+         || op->operand[0].type == OPT_OFFSET
+         || op->operand[0].type == OPT_LABEL))
+      op->operand[0].lmod = OPLM_DWORD;
+    break;
+
+  // alignment
+  case OP_MOV:
+    if (op->operand[0].type == OPT_REG && op->operand[1].type == OPT_REG
+     && op->operand[0].reg == xDI && op->operand[1].reg == xDI)
+    {
+      op->flags |= OPF_RMD;
+    }
+    break;
+
   default:
     break;
   }
@@ -1120,8 +1145,9 @@ static struct parsed_equ *equ_find(struct parsed_op *po, const char *name,
 
 static void stack_frame_access(struct parsed_op *po,
   enum opr_lenmod lmod, char *buf, size_t buf_size,
-  const char *name, int is_src, int is_lea)
+  const char *name, const char *cast, int is_src, int is_lea)
 {
+  enum opr_lenmod tmp_lmod = OPLM_UNSPEC;
   const char *prefix = "";
   char ofs_reg[16] = { 0, };
   struct parsed_equ *eq;
@@ -1151,7 +1177,7 @@ static void stack_frame_access(struct parsed_op *po,
       ferr(po, "offset %d (%s) doesn't map to any arg\n",
         offset, bp_arg);
     if (ofs_reg[0] != 0)
-      ferr(po, "offset reg on arg acecss?\n");
+      ferr(po, "offset reg on arg access?\n");
 
     for (i = arg_s = 0; i < g_func_pp.argc; i++) {
       if (g_func_pp.arg[i].reg != NULL)
@@ -1165,7 +1191,44 @@ static void stack_frame_access(struct parsed_op *po,
     if (is_lea)
       ferr(po, "lea to arg?\n");
 
-    snprintf(buf, buf_size, "%sa%d", is_src ? "(u32)" : "", i + 1);
+    switch (lmod)
+    {
+    case OPLM_BYTE:
+      if (is_src && (offset & 3) == 0)
+        snprintf(buf, buf_size, "(u8)a%d", i + 1);
+      else
+        snprintf(buf, buf_size, "BYTE%d(a%d)", offset & 3, i + 1);
+      break;
+
+    case OPLM_WORD:
+      if (offset & 1)
+        ferr(po, "unaligned arg access\n");
+      if (is_src && (offset & 2) == 0)
+        snprintf(buf, buf_size, "(u16)a%d", i + 1);
+      else
+        snprintf(buf, buf_size, "%sWORD(a%d)",
+          (offset & 2) ? "HI" : "LO", i + 1);
+      break;
+
+    case OPLM_DWORD:
+      if (offset & 3)
+        ferr(po, "unaligned arg access\n");
+      if (cast[0])
+        prefix = cast;
+      else if (is_src)
+        prefix = "(u32)";
+      snprintf(buf, buf_size, "%sa%d", prefix, i + 1);
+      break;
+
+    default:
+      ferr(po, "bp_arg bad lmod: %d\n", lmod);
+    }
+
+    // common problem
+    guess_lmod_from_c_type(&tmp_lmod, &g_func_pp.arg[i].type);
+    if ((offset & 3) && tmp_lmod != OPLM_DWORD)
+      ferr(po, "bp_arg arg/w offset %d and type '%s'\n",
+        offset, g_func_pp.arg[i].type.name);
   }
   else {
     if (g_stack_fsz == 0)
@@ -1177,46 +1240,65 @@ static void stack_frame_access(struct parsed_op *po,
 
     if (is_lea)
       prefix = "(u32)&";
+    else
+      prefix = cast;
 
     switch (lmod)
     {
     case OPLM_BYTE:
-      if (ofs_reg[0] != 0)
-        snprintf(buf, buf_size, "%ssf.b[%d+%s]",
-          prefix, sf_ofs, ofs_reg);
-      else
-        snprintf(buf, buf_size, "%ssf.b[%d]",
-          prefix, sf_ofs);
+      snprintf(buf, buf_size, "%ssf.b[%d%s%s]",
+        prefix, sf_ofs, ofs_reg[0] ? "+" : "", ofs_reg);
       break;
+
     case OPLM_WORD:
-      if (ofs_reg[0] != 0)
-        snprintf(buf, buf_size, "%ssf.w[%d+%s/2]",
-          prefix, sf_ofs / 2, ofs_reg);
-      else
-        snprintf(buf, buf_size, "%ssf.w[%d]",
-          prefix, sf_ofs / 2);
+      if ((sf_ofs & 1) || ofs_reg[0] != 0) {
+        // known unaligned or possibly unaligned
+        strcat(g_comment, " unaligned");
+        if (prefix[0] == 0)
+          prefix = "*(u16 *)&";
+        snprintf(buf, buf_size, "%ssf.b[%d%s%s]",
+          prefix, sf_ofs, ofs_reg[0] ? "+" : "", ofs_reg);
+        break;
+      }
+      snprintf(buf, buf_size, "%ssf.w[%d]", prefix, sf_ofs / 2);
       break;
+
     case OPLM_DWORD:
-      if (ofs_reg[0] != 0)
-        snprintf(buf, buf_size, "%ssf.d[%d+%s/4]",
-          prefix, sf_ofs / 4, ofs_reg);
-      else
-        snprintf(buf, buf_size, "%ssf.d[%d]",
-          prefix, sf_ofs / 4);
+      if ((sf_ofs & 3) || ofs_reg[0] != 0) {
+        // known unaligned or possibly unaligned
+        strcat(g_comment, " unaligned");
+        if (prefix[0] == 0)
+          prefix = "*(u32 *)&";
+        snprintf(buf, buf_size, "%ssf.b[%d%s%s]",
+          prefix, sf_ofs, ofs_reg[0] ? "+" : "", ofs_reg);
+        break;
+      }
+      snprintf(buf, buf_size, "%ssf.d[%d]", prefix, sf_ofs / 4);
       break;
+
     default:
       ferr(po, "bp_stack bad lmod: %d\n", lmod);
     }
   }
 }
 
+static void check_label_read_ref(struct parsed_op *po, const char *name)
+{
+  if (IS_START(name, "sub_"))
+    ferr(po, "func reference?\n");
+}
+
 static char *out_src_opr(char *buf, size_t buf_size,
-       struct parsed_op *po, struct parsed_opr *popr, int is_lea)
+       struct parsed_op *po, struct parsed_opr *popr, const char *cast,
+  int is_lea)
 {
   char tmp1[256], tmp2[256];
   char expr[256];
   int ret;
 
+  if (cast == NULL)
+    cast = "";
+
   switch (popr->type) {
   case OPT_REG:
     if (is_lea)
@@ -1224,7 +1306,7 @@ static char *out_src_opr(char *buf, size_t buf_size,
 
     switch (popr->lmod) {
     case OPLM_DWORD:
-      snprintf(buf, buf_size, "%s", opr_reg_p(po, popr));
+      snprintf(buf, buf_size, "%s%s", cast, opr_reg_p(po, popr));
       break;
     case OPLM_WORD:
       snprintf(buf, buf_size, "(u16)%s", opr_reg_p(po, popr));
@@ -1243,7 +1325,7 @@ static char *out_src_opr(char *buf, size_t buf_size,
   case OPT_REGMEM:
     if (parse_stack_el(popr->name, NULL)) {
       stack_frame_access(po, popr->lmod, buf, buf_size,
-        popr->name, 1, is_lea);
+        popr->name, cast, 1, is_lea);
       break;
     }
 
@@ -1262,28 +1344,38 @@ static char *out_src_opr(char *buf, size_t buf_size,
       break;
     }
 
-    snprintf(buf, buf_size, "%s(%s)",
-      lmod_cast_u_ptr(po, popr->lmod), expr);
+    if (cast[0] == 0)
+      cast = lmod_cast_u_ptr(po, popr->lmod);
+    snprintf(buf, buf_size, "%s(%s)", cast, expr);
     break;
 
   case OPT_LABEL:
+    check_label_read_ref(po, popr->name);
+    if (cast[0] == 0 && popr->is_ptr)
+      cast = "(u32)";
     if (is_lea)
       snprintf(buf, buf_size, "(u32)&%s", popr->name);
     else
-      snprintf(buf, buf_size, "(u32)%s", popr->name);
+      snprintf(buf, buf_size, "%s%s%s", cast, popr->name,
+        popr->is_array ? "[0]" : "");
     break;
 
   case OPT_OFFSET:
+    check_label_read_ref(po, popr->name);
+    if (cast[0] == 0)
+      cast = "(u32)";
     if (is_lea)
       ferr(po, "lea an offset?\n");
-    snprintf(buf, buf_size, "(u32)&%s", popr->name);
+    snprintf(buf, buf_size, "%s&%s", cast, popr->name);
     break;
 
   case OPT_CONST:
     if (is_lea)
       ferr(po, "lea from const?\n");
 
-    printf_number(buf, buf_size, popr->val);
+    snprintf(buf, buf_size, "%s", cast);
+    ret = strlen(buf);
+    printf_number(buf + ret, buf_size - ret, popr->val);
     break;
 
   default:
@@ -1321,14 +1413,15 @@ static char *out_dst_opr(char *buf, size_t buf_size,
   case OPT_REGMEM:
     if (parse_stack_el(popr->name, NULL)) {
       stack_frame_access(po, popr->lmod, buf, buf_size,
-        popr->name, 0, 0);
+        popr->name, "", 0, 0);
       break;
     }
 
-    return out_src_opr(buf, buf_size, po, popr, 0);
+    return out_src_opr(buf, buf_size, po, popr, NULL, 0);
 
   case OPT_LABEL:
-    snprintf(buf, buf_size, "%s", popr->name);
+    snprintf(buf, buf_size, "%s%s", popr->name,
+      popr->is_array ? "[0]" : "");
     break;
 
   default:
@@ -1338,6 +1431,12 @@ static char *out_dst_opr(char *buf, size_t buf_size,
   return buf;
 }
 
+static char *out_src_opr_u32(char *buf, size_t buf_size,
+       struct parsed_op *po, struct parsed_opr *popr)
+{
+  return out_src_opr(buf, buf_size, po, popr, NULL, 0);
+}
+
 static enum parsed_flag_op split_cond(struct parsed_op *po,
   enum op_op op, int *is_inv)
 {
@@ -1483,19 +1582,19 @@ static void out_cmp_test(char *buf, size_t buf_size,
 
   if (po->op == OP_TEST) {
     if (IS(opr_name(po, 0), opr_name(po, 1))) {
-      out_src_opr(buf3, sizeof(buf3), po, &po->operand[0], 0);
+      out_src_opr_u32(buf3, sizeof(buf3), po, &po->operand[0]);
     }
     else {
-      out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], 0);
-      out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], 0);
+      out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
+      out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
       snprintf(buf3, sizeof(buf3), "(%s & %s)", buf1, buf2);
     }
     out_test_for_cc(buf, buf_size, po, pfo, is_inv,
       po->operand[0].lmod, buf3);
   }
   else if (po->op == OP_CMP) {
-    out_src_opr(buf2, sizeof(buf2), po, &po->operand[0], 0);
-    out_src_opr(buf3, sizeof(buf3), po, &po->operand[1], 0);
+    out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[0]);
+    out_src_opr_u32(buf3, sizeof(buf3), po, &po->operand[1]);
     out_cmp_for_cc(buf, buf_size, po, pfo, is_inv,
       po->operand[0].lmod, buf2, buf3);
   }
@@ -1780,19 +1879,27 @@ static int scan_for_reg_clear(int i, int reg)
 // scan for positive, constant esp adjust
 static int scan_for_esp_adjust(int i, int opcnt, int *adj)
 {
+  struct parsed_op *po;
+  *adj = 0;
+
   for (; i < opcnt; i++) {
-    if (ops[i].op == OP_ADD && ops[i].operand[0].reg == xSP) {
-      if (ops[i].operand[1].type != OPT_CONST)
+    po = &ops[i];
+
+    if (po->op == OP_ADD && po->operand[0].reg == xSP) {
+      if (po->operand[1].type != OPT_CONST)
         ferr(&ops[i], "non-const esp adjust?\n");
-      *adj = ops[i].operand[1].val;
+      *adj += po->operand[1].val;
       if (*adj & 3)
         ferr(&ops[i], "unaligned esp adjust: %x\n", *adj);
       return i;
     }
-
-    if ((ops[i].flags & (OPF_JMP|OPF_TAIL))
-         || ops[i].op == OP_PUSH || ops[i].op == OP_POP)
+    else if (po->op == OP_PUSH)
+      *adj -= lmod_bytes(po, po->operand[0].lmod);
+    else if (po->op == OP_POP)
+      *adj += lmod_bytes(po, po->operand[0].lmod);
+    else if ((po->flags & (OPF_JMP|OPF_TAIL)) && po->op != OP_CALL)
       return -1;
+
     if (g_labels[i][0] != 0)
       return -1;
   }
@@ -1818,7 +1925,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
 {
   struct parsed_op *po, *delayed_flag_op = NULL, *tmp_op;
   struct parsed_opr *last_arith_dst = NULL;
-  char buf1[256], buf2[256], buf3[256];
+  char buf1[256], buf2[256], buf3[256], cast[64];
   struct parsed_proto *pp, *pp_tmp;
   struct parsed_data *pd;
   const char *tmpname;
@@ -1827,6 +1934,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   int cmp_result_vars = 0;
   int need_mul_var = 0;
   int had_decl = 0;
+  int label_pending = 0;
   int regmask_save = 0;
   int regmask_arg = 0;
   int regmask = 0;
@@ -1846,11 +1954,11 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   if (ret)
     ferr(ops, "proto_parse failed for '%s'\n", funcn);
 
-  fprintf(fout, "%s %s(", g_func_pp.ret_type, funcn);
+  fprintf(fout, "%s %s(", g_func_pp.ret_type.name, funcn);
   for (i = 0; i < g_func_pp.argc; i++) {
     if (i > 0)
       fprintf(fout, ", ");
-    fprintf(fout, "%s a%d", g_func_pp.arg[i].type, i + 1);
+    fprintf(fout, "%s a%d", g_func_pp.arg[i].type.name, i + 1);
   }
   fprintf(fout, ")\n{\n");
 
@@ -2049,10 +2157,10 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         j /= 4;
         if (j > ARRAY_SIZE(pp->arg))
           ferr(po, "esp adjust too large?\n");
-        pp->ret_type = strdup("int");
+        pp->ret_type.name = strdup("int");
         pp->argc = pp->argc_stack = j;
         for (arg = 0; arg < pp->argc; arg++)
-          pp->arg[arg].type = strdup("int");
+          pp->arg[arg].type.name = strdup("int");
       }
       else {
         ret = proto_parse(fhdr, tmpname, pp);
@@ -2060,13 +2168,37 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
           ferr(po, "proto_parse failed for call '%s'\n", tmpname);
       }
 
-      ret = scan_for_esp_adjust(i + 1, opcnt, &j);
+      // look for and make use of esp adjust
+      ret = -1;
+      if (!pp->is_stdcall)
+        ret = scan_for_esp_adjust(i + 1, opcnt, &j);
       if (ret >= 0) {
+        if (pp->is_vararg) {
+          if (j / 4 < pp->argc_stack)
+            ferr(po, "esp adjust is too small: %x < %x\n",
+              j, pp->argc_stack * 4);
+          // modify pp to make it have varargs as normal args
+          arg = pp->argc;
+          pp->argc += j / 4 - pp->argc_stack;
+          for (; arg < pp->argc; arg++) {
+            pp->arg[arg].type.name = strdup("int");
+            pp->argc_stack++;
+          }
+          if (pp->argc > ARRAY_SIZE(pp->arg))
+            ferr(po, "too many args\n");
+        }
         if (pp->argc_stack != j / 4)
           ferr(po, "stack tracking failed: %x %x\n",
-            pp->argc_stack, j);
+            pp->argc_stack * 4, j);
+
         ops[ret].flags |= OPF_RMD;
+        // a bit of a hack, but deals with use of
+        // single adj for multiple calls
+        ops[ret].operand[1].val -= j;
       }
+      else if (pp->is_vararg)
+        ferr(po, "missing esp_adjust for vararg func '%s'\n",
+          pp->name);
 
       // can't call functions with non-__cdecl callbacks yet
       for (arg = 0; arg < pp->argc; arg++) {
@@ -2205,6 +2337,9 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         if (scan_for_mod(tmp_op, ret + 1, i) >= 0)
           pfomask = 1 << pfo;
       }
+      else if (tmp_op->op == OP_CMPS) {
+        pfomask = 1 << PFO_Z;
+      }
       else {
         if ((pfo != PFO_Z && pfo != PFO_S && pfo != PFO_P)
             || scan_for_mod_opr0(tmp_op, ret + 1, i) >= 0)
@@ -2227,11 +2362,11 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
     else if (po->op == OP_CALL && po->operand[0].type != OPT_LABEL) {
       pp = po->datap;
       my_assert_not(pp, NULL);
-      fprintf(fout, "  %s (*icall%d)(", pp->ret_type, i);
+      fprintf(fout, "  %s (*icall%d)(", pp->ret_type.name, i);
       for (j = 0; j < pp->argc; j++) {
         if (j > 0)
           fprintf(fout, ", ");
-        fprintf(fout, "%s a%d", pp->arg[j].type, j + 1);
+        fprintf(fout, "%s a%d", pp->arg[j].type.name, j + 1);
       }
       fprintf(fout, ");\n");
     }
@@ -2284,7 +2419,9 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   }
 
   // declare other regs - special case for eax
-  if (!((regmask | regmask_arg) & 1) && !IS(g_func_pp.ret_type, "void")) {
+  if (!((regmask | regmask_arg) & 1)
+   && !IS(g_func_pp.ret_type.name, "void"))
+  {
     fprintf(fout, "  u32 eax = 0;\n");
     had_decl = 1;
   }
@@ -2340,8 +2477,10 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   // output ops
   for (i = 0; i < opcnt; i++)
   {
-    if (g_labels[i][0] != 0 && g_label_refs[i].i != -1)
+    if (g_labels[i][0] != 0 && g_label_refs[i].i != -1) {
       fprintf(fout, "\n%s:\n", g_labels[i]);
+      label_pending = 1;
+    }
 
     po = &ops[i];
     if (po->flags & OPF_RMD)
@@ -2371,13 +2510,13 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
       else if (last_arith_dst != NULL
         && (pfo == PFO_Z || pfo == PFO_S || pfo == PFO_P))
       {
-        out_src_opr(buf3, sizeof(buf3), po, last_arith_dst, 0);
+        out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst);
         out_test_for_cc(buf1, sizeof(buf1), po, pfo, is_inv,
           last_arith_dst->lmod, buf3);
         is_delayed = 1;
       }
       else if (po->datap != NULL) {
-        // use preprocessed results
+        // use preprocessed flag calc results
         tmp_op = po->datap;
         if (!tmp_op || !(tmp_op->pfomask & (1 << pfo)))
           ferr(po, "not prepared for pfo %d\n", pfo);
@@ -2414,10 +2553,10 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
       case OP_MOV:
         assert_operand_cnt(2);
         propagate_lmod(po, &po->operand[0], &po->operand[1]);
-        fprintf(fout, "  %s = %s%s;",
+        fprintf(fout, "  %s = %s;",
             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
-            po->operand[0].is_ptr ? "(void *)" : "",
-            out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], 0));
+            out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
+              po->operand[0].is_ptr ? "(void *)" : "", 0));
         break;
 
       case OP_LEA:
@@ -2425,14 +2564,15 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         po->operand[1].lmod = OPLM_DWORD; // always
         fprintf(fout, "  %s = %s;",
             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
-            out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], 1));
+            out_src_opr(buf2, sizeof(buf2), po, &po->operand[1],
+              NULL, 1));
         break;
 
       case OP_MOVZX:
         assert_operand_cnt(2);
         fprintf(fout, "  %s = %s;",
             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
-            out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], 0));
+            out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
         break;
 
       case OP_MOVSX:
@@ -2450,7 +2590,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         fprintf(fout, "  %s = %s%s;",
             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
             buf3,
-            out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], 0));
+            out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
         break;
 
       case OP_NOT:
@@ -2463,7 +2603,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         assert_operand_cnt(2);
         fprintf(fout, "  %s = (s32)%s >> 31;",
             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
-            out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], 0));
+            out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
         strcpy(g_comment, "cdq");
         break;
 
@@ -2505,6 +2645,35 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         }
         break;
 
+      case OP_CMPS:
+        // assumes DF=0
+        // repe ~ repeat while ZF=1
+        assert_operand_cnt(3);
+        j = lmod_bytes(po, po->operand[0].lmod);
+        strcpy(buf1, lmod_cast_u_ptr(po, po->operand[0].lmod));
+        if (po->flags & OPF_REP) {
+          fprintf(fout,
+            "  for (; ecx != 0; ecx--, edi += %d, esi += %d)\n",
+            j, j);
+          fprintf(fout,
+            "    if ((cond_z = (%sedi == %sesi)) %s 0)\n",
+              buf1, buf1, (po->flags & OPF_REPZ) ? "==" : "!=");
+          fprintf(fout,
+            "      break;");
+          snprintf(g_comment, sizeof(g_comment), "rep%s cmps",
+            (po->flags & OPF_REPZ) ? "e" : "ne");
+        }
+        else {
+          fprintf(fout,
+            "    cond_z = (%sedi = %sesi); edi += %d; esi += %d;",
+            buf1, buf1, j, j);
+          strcpy(g_comment, "cmps");
+        }
+        pfomask &= ~(1 << PFO_Z);
+        last_arith_dst = NULL;
+        delayed_flag_op = NULL;
+        break;
+
       // arithmetic w/flags
       case OP_ADD:
       case OP_SUB:
@@ -2519,7 +2688,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         fprintf(fout, "  %s %s= %s;",
             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
             op_to_c(po),
-            out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], 0));
+            out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
         last_arith_dst = &po->operand[0];
         delayed_flag_op = NULL;
         break;
@@ -2529,7 +2698,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
         fprintf(fout, "  %s = %s%s >> %s;", buf1,
           lmod_cast_s(po, po->operand[0].lmod), buf1,
-          out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], 0));
+          out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
         last_arith_dst = &po->operand[0];
         delayed_flag_op = NULL;
         break;
@@ -2573,7 +2742,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         fprintf(fout, "  %s %s= %s + cond_c;",
             out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
             op_to_c(po),
-            out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], 0));
+            out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
         last_arith_dst = &po->operand[0];
         delayed_flag_op = NULL;
         break;
@@ -2595,7 +2764,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
 
       case OP_NEG:
         out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
-        out_src_opr(buf2, sizeof(buf2), po, &po->operand[0], 0);
+        out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[0]);
         fprintf(fout, "  %s = -%s%s;", buf1,
           lmod_cast_s(po, po->operand[0].lmod), buf2);
         last_arith_dst = &po->operand[0];
@@ -2616,7 +2785,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         assert_operand_cnt(1);
         strcpy(buf1, po->op == OP_IMUL ? "(s64)(s32)" : "(u64)");
         fprintf(fout, "  mul_tmp = %seax * %s%s;\n", buf1, buf1,
-          out_src_opr(buf2, sizeof(buf2), po, &po->operand[0], 0));
+          out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[0]));
         fprintf(fout, "  edx = mul_tmp >> 32;\n");
         fprintf(fout, "  eax = mul_tmp;");
         last_arith_dst = NULL;
@@ -2635,7 +2804,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         else
           ret = scan_for_cdq_edx(i - 1);
         if (ret >= 0) {
-          out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], 0);
+          out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
           strcpy(buf2, lmod_cast(po, po->operand[0].lmod,
             po->op == OP_IDIV));
           fprintf(fout, "  edx = %seax %% %s%s;\n", buf2, buf2, buf1);
@@ -2662,6 +2831,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         }
         else
           no_output = 1;
+        last_arith_dst = NULL;
         delayed_flag_op = po;
         break;
 
@@ -2697,15 +2867,15 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
 
         if (po->operand[0].type != OPT_LABEL)
           fprintf(fout, "  icall%d = (void *)%s;\n", i,
-            out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], 0));
+            out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]));
 
         fprintf(fout, "  ");
-        if (!IS(pp->ret_type, "void")) {
+        if (!IS(pp->ret_type.name, "void")) {
           if (po->flags & OPF_TAIL)
             fprintf(fout, "return ");
           else
             fprintf(fout, "eax = ");
-          if (strchr(pp->ret_type, '*'))
+          if (pp->ret_type.is_ptr)
             fprintf(fout, "(u32)");
         }
 
@@ -2722,11 +2892,12 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
           if (arg > 0)
             fprintf(fout, ", ");
 
-          if (strchr(pp->arg[arg].type, '*'))
-            fprintf(fout, "(%s)", pp->arg[arg].type);
+          cast[0] = 0;
+          if (pp->arg[arg].type.is_ptr)
+            snprintf(cast, sizeof(cast), "(%s)", pp->arg[arg].type.name);
 
           if (pp->arg[arg].reg != NULL) {
-            fprintf(fout, "%s", pp->arg[arg].reg);
+            fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
             continue;
           }
 
@@ -2735,31 +2906,36 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
           if (tmp_op == NULL)
             ferr(po, "parsed_op missing for arg%d\n", arg);
           if (tmp_op->argmask) {
-            fprintf(fout, "s_a%d", arg + 1);
+            fprintf(fout, "%ss_a%d", cast, arg + 1);
           }
           else {
             fprintf(fout, "%s",
               out_src_opr(buf1, sizeof(buf1),
-                tmp_op, &tmp_op->operand[0], 0));
+                tmp_op, &tmp_op->operand[0], cast, 0));
           }
         }
         fprintf(fout, ");");
 
         if (po->flags & OPF_TAIL) {
           strcpy(g_comment, "tailcall");
-          if (IS(pp->ret_type, "void"))
+          if (IS(pp->ret_type.name, "void")) {
             fprintf(fout, "\n  return;");
+            strcpy(g_comment, "^ tailcall");
+          }
         }
         delayed_flag_op = NULL;
         last_arith_dst = NULL;
         break;
 
       case OP_RET:
-        if (IS(g_func_pp.ret_type, "void"))
-          fprintf(fout, "  return;");
-        else if (strchr(g_func_pp.ret_type, '*'))
+        if (IS(g_func_pp.ret_type.name, "void")) {
+          if (i != opcnt - 1 || label_pending)
+            fprintf(fout, "  return;");
+        }
+        else if (g_func_pp.ret_type.is_ptr) {
           fprintf(fout, "  return (%s)eax;",
-            g_func_pp.ret_type);
+            g_func_pp.ret_type.name);
+        }
         else
           fprintf(fout, "  return eax;");
         break;
@@ -2767,7 +2943,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
       case OP_PUSH:
         if (po->argmask) {
           // special case - saved func arg
-          out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], 0);
+          out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
           for (j = 0; j < 32; j++) {
             if (po->argmask & (1 << j))
               fprintf(fout, "  s_a%d = %s;", j + 1, buf1);
@@ -2775,7 +2951,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
           break;
         }
         else if (po->flags & OPF_RSAVE) {
-          out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], 0);
+          out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
           fprintf(fout, "  s_%s = %s;", buf1, buf1);
           break;
         }
@@ -2784,7 +2960,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
 
       case OP_POP:
         if (po->flags & OPF_RSAVE) {
-          out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], 0);
+          out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
           fprintf(fout, "  %s = s_%s;", buf1, buf1);
           break;
         }
@@ -2801,6 +2977,13 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
         break;
     }
 
+    // some sanity checking
+    if ((po->flags & OPF_REP) && po->op != OP_STOS
+        && po->op != OP_MOVS && po->op != OP_CMPS)
+      ferr(po, "unexpected rep\n");
+    if ((po->flags & (OPF_REPZ|OPF_REPNZ)) && po->op != OP_CMPS)
+      ferr(po, "unexpected repz/repnz\n");
+
     if (g_comment[0] != 0) {
       fprintf(fout, "  // %s", g_comment);
       g_comment[0] = 0;
@@ -2810,7 +2993,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
       fprintf(fout, "\n");
 
     if (pfomask != 0)
-        ferr(po, "missed flag calc, pfomask=%x\n", pfomask);
+      ferr(po, "missed flag calc, pfomask=%x\n", pfomask);
 
     // see is delayed flag stuff is still valid
     if (delayed_flag_op != NULL && delayed_flag_op != po) {
@@ -2822,6 +3005,8 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
       if (is_opr_modified(last_arith_dst, po))
         last_arith_dst = NULL;
     }
+
+    label_pending = 0;
   }
 
   fprintf(fout, "}\n\n");
@@ -3047,8 +3232,8 @@ parse_words:
     }
 
     // alow asm patches in comments
-    if (*p == ';' && IS_START(p, "; sctpatch: ")) {
-      p = sskip(p + 12);
+    if (*p == ';' && IS_START(p, "; sctpatch:")) {
+      p = sskip(p + 11);
       if (*p == 0 || *p == ';')
         continue;
       goto parse_words; // lame