reg return, __userstack, repe fix, more..
authornotaz <notasas@gmail.com>
Sun, 16 Feb 2014 02:18:27 +0000 (04:18 +0200)
committernotaz <notasas@gmail.com>
Sun, 16 Feb 2014 02:21:04 +0000 (04:21 +0200)
tools/mkbridge.c
tools/protoparse.h
tools/translate.c

index 9b87445..a5dd2b8 100644 (file)
@@ -10,6 +10,8 @@
 
 #include "protoparse.h"
 
+static const char *c_save_regs[] = { "ebx", "esi", "edi", "ebp" };
+
 static int is_x86_reg_saved(const char *reg)
 {
        static const char *nosave_regs[] = { "eax", "edx", "ecx" };
@@ -79,7 +81,7 @@ static void out_toasm_x86(FILE *f, const char *sym_out,
        }
 
        if (pp->argc_stack == 0 && !must_save && !pp->is_stdcall
-            && !pp->is_vararg)
+            && !pp->is_vararg && !pp->has_retreg)
        {
                // load arg regs
                for (i = 0; i < pp->argc; i++) {
@@ -90,15 +92,17 @@ static void out_toasm_x86(FILE *f, const char *sym_out,
                return;
        }
 
+       // asm_stack_args | saved_regs | ra | args_from_c
+
        // save the regs
-       for (i = 0; i < pp->argc; i++) {
-               if (pp->arg[i].reg != NULL && is_x86_reg_saved(pp->arg[i].reg)) {
-                       fprintf(f, "\tpushl %%%s\n", pp->arg[i].reg);
-                       sarg_ofs++;
-               }
+       // because we don't always know what we are calling,
+       // be safe and save everything that has to be saved in __cdecl
+       for (i = 0; i < ARRAY_SIZE(c_save_regs); i++) {
+               fprintf(f, "\tpushl %%%s\n", c_save_regs[i]);
+               sarg_ofs++;
        }
 
-       // reconstruct arg stack
+       // reconstruct arg stack for asm
        for (i = argc_repush - 1; i >= 0; i--) {
                if (pp->arg[i].reg == NULL) {
                        fprintf(f, "\tmovl %d(%%esp), %%eax\n",
@@ -108,36 +112,52 @@ static void out_toasm_x86(FILE *f, const char *sym_out,
                        args_repushed++;
                }
        }
-       // my_assert(args_repushed, pp->argc_stack);
 
        // load arg regs
        for (i = 0; i < pp->argc; i++) {
                if (pp->arg[i].reg != NULL) {
                        fprintf(f, "\tmovl %d(%%esp), %%%s\n",
                                (i + sarg_ofs) * 4, pp->arg[i].reg);
+                       if (pp->arg[i].type.is_retreg)
+                               fprintf(f, "\tmovl (%%%s), %%%s\n",
+                                       pp->arg[i].reg, pp->arg[i].reg);
                }
        }
 
        fprintf(f, "\n\t# %s\n", pp->is_stdcall ? "__stdcall" : "__cdecl");
        fprintf(f, "\tcall %s\n\n", sym_out);
 
-       if (args_repushed && !pp->is_stdcall)
+       if (args_repushed && !pp->is_stdcall) {
                fprintf(f, "\tadd $%d,%%esp\n", args_repushed * 4);
+               sarg_ofs -= args_repushed;
+       }
 
-       // restore regs
-       for (i = pp->argc - 1; i >= 0; i--) {
-               if (pp->arg[i].reg != NULL && is_x86_reg_saved(pp->arg[i].reg))
-                       fprintf(f, "\tpopl %%%s\n", pp->arg[i].reg);
+       // update the retreg regs
+       if (pp->has_retreg) {
+               for (i = 0; i < pp->argc; i++) {
+                       if (pp->arg[i].type.is_retreg) {
+                               fprintf(f, "\tmovl %d(%%esp), %%ecx\n"
+                                          "\tmovl %%%s, (%%ecx)\n",
+                                       (i + sarg_ofs) * 4, pp->arg[i].reg);
+                       }
+               }
        }
 
+       // restore regs
+       for (i = ARRAY_SIZE(c_save_regs) - 1; i >= 0; i--)
+               fprintf(f, "\tpopl %%%s\n", c_save_regs[i]);
+
        fprintf(f, "\tret\n\n");
 }
 
 static void out_fromasm_x86(FILE *f, const char *sym,
        const struct parsed_proto *pp)
 {
+       int reg_ofs[ARRAY_SIZE(pp->arg)];
        int sarg_ofs = 1; // stack offset to args, in DWORDs
        int saved_regs = 0;
+       int ecx_ofs = -1;
+       int edx_ofs = -1;
        int c_is_stdcall;
        int argc_repush;
        int stack_args;
@@ -176,10 +196,32 @@ static void out_fromasm_x86(FILE *f, const char *sym,
        fprintf(f, "\tpushl %%ecx\n");
        saved_regs++;
        sarg_ofs++;
+       ecx_ofs = sarg_ofs;
        if (!ret64) {
                fprintf(f, "\tpushl %%edx\n");
                saved_regs++;
                sarg_ofs++;
+               edx_ofs = sarg_ofs;
+       }
+
+       // need space for retreg args
+       if (pp->has_retreg) {
+               for (i = 0; i < pp->argc; i++) {
+                       if (!pp->arg[i].type.is_retreg)
+                               continue;
+                       if (IS(pp->arg[i].reg, "ecx") && ecx_ofs >= 0) {
+                               reg_ofs[i] = ecx_ofs;
+                               continue;
+                       }
+                       if (IS(pp->arg[i].reg, "edx") && edx_ofs >= 0) {
+                               reg_ofs[i] = edx_ofs;
+                               continue;
+                       }
+                       fprintf(f, "\tpushl %%%s\n", pp->arg[i].reg);
+                       saved_regs++;
+                       sarg_ofs++;
+                       reg_ofs[i] = sarg_ofs;
+               }
        }
 
        // construct arg stack
@@ -191,12 +233,18 @@ static void out_fromasm_x86(FILE *f, const char *sym,
                        stack_args--;
                }
                else {
-                       if (IS(pp->arg[i].reg, "ecx"))
+                       const char *reg = pp->arg[i].reg;
+                       if (pp->arg[i].type.is_retreg) {
+                               reg = "ecx";
+                               fprintf(f, "\tlea %d(%%esp), %%ecx\n",
+                                 (sarg_ofs - reg_ofs[i]) * 4);
+                       }
+                       else if (IS(reg, "ecx"))
                                // must reload original ecx
                                fprintf(f, "\tmovl %d(%%esp), %%ecx\n",
                                        (sarg_ofs - 2) * 4);
 
-                       fprintf(f, "\tpushl %%%s\n", pp->arg[i].reg);
+                       fprintf(f, "\tpushl %%%s\n", reg);
                }
                sarg_ofs++;
        }
@@ -207,6 +255,21 @@ static void out_fromasm_x86(FILE *f, const char *sym,
                fprintf(f, "\tadd $%d,%%esp\n",
                        (sarg_ofs - (saved_regs + 1)) * 4);
 
+       // pop retregs
+       if (pp->has_retreg) {
+               for (i = pp->argc - 1; i >= 0; i--) {
+                       if (!pp->arg[i].type.is_retreg)
+                               continue;
+                       if (IS(pp->arg[i].reg, "ecx") && ecx_ofs >= 0) {
+                               continue;
+                       }
+                       if (IS(pp->arg[i].reg, "edx") && edx_ofs >= 0) {
+                               continue;
+                       }
+                       fprintf(f, "\tpopl %%%s\n", pp->arg[i].reg);
+               }
+       }
+
        if (!ret64)
                fprintf(f, "\tpopl %%edx\n");
        fprintf(f, "\tpopl %%ecx\n");
index d58bf83..b0e794e 100644 (file)
@@ -6,6 +6,7 @@ struct parsed_type {
        unsigned int is_array:1;
        unsigned int is_ptr:1;
        unsigned int is_struct:1; // split for args
+       unsigned int is_retreg:1; // register to return
 };
 
 struct parsed_proto_arg {
@@ -32,8 +33,10 @@ struct parsed_proto {
        unsigned int is_fptr:1;
        unsigned int is_noreturn:1;
        unsigned int is_unresolved:1;
+       unsigned int is_userstack:1;
        unsigned int is_arg:1;        // decl in func arg
        unsigned int has_structarg:1;
+       unsigned int has_retreg:1;
 };
 
 static const char *hdrfn;
@@ -113,14 +116,22 @@ static int do_protostrs(FILE *fhdr, const char *fname)
        return -1;
 }
 
-static int get_regparm(char *dst, size_t dlen, char *p)
+static int get_regparm(char *dst, size_t dlen, char *p, int *retreg)
 {
-       int i, o;
+       int i = 0, o;
+
+       *retreg = 0;
 
        if (*p != '<')
                return 0;
 
-       for (o = 0, i = 1; o < dlen; i++) {
+       i++;
+       if (p[i] == '*') {
+               *retreg = 1;
+               i++;
+       }
+
+       for (o = 0; o < dlen; i++) {
                if (p[i] == 0)
                        return 0;
                if (p[i] == '>')
@@ -302,6 +313,7 @@ static int parse_protostr(char *protostr, struct parsed_proto *pp)
        char regparm[16];
        char buf[256];
        char cconv[32];
+       int is_retreg;
        int xarg = 0;
        char *p, *p1;
        int i, l;
@@ -390,6 +402,10 @@ static int parse_protostr(char *protostr, struct parsed_proto *pp)
                pp->is_stdcall = 1; // IDA
        else if (IS(cconv, "__usercall"))
                pp->is_stdcall = 0; // IDA
+       else if (IS(cconv, "__userstack")) {
+               pp->is_stdcall = 0; // custom
+               pp->is_userstack = 1;
+       }
        else if (IS(cconv, "WINAPI"))
                pp->is_stdcall = 1;
        else {
@@ -420,7 +436,7 @@ static int parse_protostr(char *protostr, struct parsed_proto *pp)
        }
        strcpy(pp->name, buf);
 
-       ret = get_regparm(regparm, sizeof(regparm), p);
+       ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
        if (ret > 0) {
                if (!IS(regparm, "eax") && !IS(regparm, "ax")
                 && !IS(regparm, "al") && !IS(regparm, "edx:eax"))
@@ -536,12 +552,14 @@ static int parse_protostr(char *protostr, struct parsed_proto *pp)
 #endif
                arg->reg = NULL;
 
-               ret = get_regparm(regparm, sizeof(regparm), p);
+               ret = get_regparm(regparm, sizeof(regparm), p, &is_retreg);
                if (ret > 0) {
                        p += ret;
                        p = sskip(p);
 
                        arg->reg = strdup(map_reg(regparm));
+                       arg->type.is_retreg = is_retreg;
+                       pp->has_retreg |= is_retreg;
                }
 
                if (strstr(arg->type.name, "int64")
index aff7b61..14babd8 100644 (file)
@@ -82,6 +82,7 @@ enum op_op {
        OP_RCR,
        OP_ADC,
        OP_SBB,
+       OP_BSF,
        OP_INC,
        OP_DEC,
        OP_NEG,
@@ -774,6 +775,7 @@ static const struct {
   { "rcr",  OP_RCR,    2, 2, OPF_DATA|OPF_FLAGS|OPF_CC },
   { "adc",  OP_ADC,    2, 2, OPF_DATA|OPF_FLAGS|OPF_CC },
   { "sbb",  OP_SBB,    2, 2, OPF_DATA|OPF_FLAGS|OPF_CC },
+  { "bsf",  OP_BSF,    2, 2, OPF_DATA|OPF_FLAGS },
   { "inc",  OP_INC,    1, 1, OPF_DATA|OPF_FLAGS },
   { "dec",  OP_DEC,    1, 1, OPF_DATA|OPF_FLAGS },
   { "neg",  OP_NEG,    1, 1, OPF_DATA|OPF_FLAGS },
@@ -2591,6 +2593,43 @@ static const struct parsed_proto *resolve_icall(int i, int opcnt,
   return pp;
 }
 
+static int try_resolve_const(int i, const struct parsed_opr *opr,
+  int magic, unsigned int *val)
+{
+  struct label_ref *lr;
+  int ret = 0;
+
+  ops[i].cc_scratch = magic;
+
+  while (1) {
+    if (g_labels[i][0] != 0) {
+      lr = &g_label_refs[i];
+      for (; lr != NULL; lr = lr->next)
+        ret |= try_resolve_const(lr->i, opr, magic, val);
+      if (i > 0 && LAST_OP(i - 1))
+        return ret;
+    }
+
+    i--;
+    if (i < 0)
+      return -1;
+
+    if (ops[i].cc_scratch == magic)
+      return 0;
+    ops[i].cc_scratch = magic;
+
+    if (!(ops[i].flags & OPF_DATA))
+      continue;
+    if (!is_opr_modified(opr, &ops[i]))
+      continue;
+    if (ops[i].op != OP_MOV && ops[i].operand[1].type != OPT_CONST)
+      return -1;
+
+    *val = ops[i].operand[1].val;
+    return 1;
+  }
+}
+
 static int collect_call_args_r(struct parsed_op *po, int i,
   struct parsed_proto *pp, int *regmask, int *save_arg_vars, int arg,
   int magic, int need_op_saving, int may_reuse)
@@ -2850,6 +2889,7 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
   struct parsed_data *pd;
   const char *tmpname;
   enum parsed_flag_op pfo;
+  unsigned int uval;
   int save_arg_vars = 0;
   int cond_vars = 0;
   int need_tmp_var = 0;
@@ -2905,6 +2945,9 @@ static void gen_func(FILE *fout, FILE *fhdr, const char *funcn, int opcnt)
       }
       fprintf(fout, ")");
     }
+    else if (g_func_pp->arg[i].type.is_retreg) {
+      fprintf(fout, "u32 *r_%s", g_func_pp->arg[i].reg);
+    }
     else {
       fprintf(fout, "%s a%d", g_func_pp->arg[i].type.name, i + 1);
     }
@@ -3189,9 +3232,18 @@ tailcall:
           if (l)
             // not resolved just to single func
             pp->is_fptr = 1;
-          if (po->operand[0].type == OPT_REG)
+
+          switch (po->operand[0].type) {
+          case OPT_REG:
             // we resolved this call and no longer need the register
             po->regmask_src &= ~(1 << po->operand[0].reg);
+            break;
+          case OPT_REGMEM:
+            pp->is_fptr = 1;
+            break;
+          default:
+            break;
+          }
         }
         if (pp == NULL) {
           pp = calloc(1, sizeof(*pp));
@@ -3291,7 +3343,7 @@ tailcall:
     }
 
     if (po->op == OP_PUSH && po->argnum == 0
-      && !(po->flags & OPF_RSAVE))
+      && !(po->flags & OPF_RSAVE) && !g_func_pp->is_userstack)
     {
       if (po->operand[0].type == OPT_REG)
       {
@@ -3574,15 +3626,26 @@ tailcall:
       }
     }
     fprintf(fout, " };\n");
+    had_decl = 1;
   }
 
   // declare stack frame, va_arg
-  if (g_stack_fsz)
+  if (g_stack_fsz) {
     fprintf(fout, "  union { u32 d[%d]; u16 w[%d]; u8 b[%d]; } sf;\n",
       (g_stack_fsz + 3) / 4, (g_stack_fsz + 1) / 2, g_stack_fsz);
+    had_decl = 1;
+  }
+
+  if (g_func_pp->is_userstack) {
+    fprintf(fout, "  u32 fake_sf[1024];\n");
+    fprintf(fout, "  u32 *esp = &fake_sf[1024];\n");
+    had_decl = 1;
+  }
 
-  if (g_func_pp->is_vararg)
+  if (g_func_pp->is_vararg) {
     fprintf(fout, "  va_list ap;\n");
+    had_decl = 1;
+  }
 
   // declare arg-registers
   for (i = 0; i < g_func_pp->argc; i++) {
@@ -3590,12 +3653,20 @@ tailcall:
       reg = char_array_i(regs_r32,
               ARRAY_SIZE(regs_r32), g_func_pp->arg[i].reg);
       if (regmask & (1 << reg)) {
-        fprintf(fout, "  u32 %s = (u32)a%d;\n",
-          g_func_pp->arg[i].reg, i + 1);
+        if (g_func_pp->arg[i].type.is_retreg)
+          fprintf(fout, "  u32 %s = *r_%s;\n",
+            g_func_pp->arg[i].reg, g_func_pp->arg[i].reg);
+        else
+          fprintf(fout, "  u32 %s = (u32)a%d;\n",
+            g_func_pp->arg[i].reg, i + 1);
       }
-      else
+      else {
+        if (g_func_pp->arg[i].type.is_retreg)
+          ferr(ops, "retreg '%s' is unused?\n",
+            g_func_pp->arg[i].reg);
         fprintf(fout, "  // %s = a%d; // unused\n",
           g_func_pp->arg[i].reg, i + 1);
+      }
       had_decl = 1;
     }
   }
@@ -3742,24 +3813,32 @@ tailcall:
     pfomask = po->pfomask;
 
     if (po->flags & (OPF_REPZ|OPF_REPNZ)) {
-      // we need initial flags for ecx=0 case..
-      if (i > 0 && ops[i - 1].op == OP_XOR
-        && IS(ops[i - 1].operand[0].name,
-              ops[i - 1].operand[1].name))
-      {
-        fprintf(fout, "  cond_z = ");
-        if (pfomask & (1 << PFO_C))
-          fprintf(fout, "cond_c = ");
-        fprintf(fout, "0;\n");
-      }
-      else if (last_arith_dst != NULL) {
-        out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst);
-        out_test_for_cc(buf1, sizeof(buf1), po, PFO_Z, 0,
-          last_arith_dst->lmod, buf3);
-        fprintf(fout, "  cond_z = %s;\n", buf1);
+      struct parsed_opr opr = {0,};
+      opr.type = OPT_REG;
+      opr.reg = xCX;
+      opr.lmod = OPLM_DWORD;
+      ret = try_resolve_const(i, &opr, opcnt * 7 + i, &uval);
+
+      if (ret != 1 || uval == 0) {
+        // we need initial flags for ecx=0 case..
+        if (i > 0 && ops[i - 1].op == OP_XOR
+          && IS(ops[i - 1].operand[0].name,
+                ops[i - 1].operand[1].name))
+        {
+          fprintf(fout, "  cond_z = ");
+          if (pfomask & (1 << PFO_C))
+            fprintf(fout, "cond_c = ");
+          fprintf(fout, "0;\n");
+        }
+        else if (last_arith_dst != NULL) {
+          out_src_opr_u32(buf3, sizeof(buf3), po, last_arith_dst);
+          out_test_for_cc(buf1, sizeof(buf1), po, PFO_Z, 0,
+            last_arith_dst->lmod, buf3);
+          fprintf(fout, "  cond_z = %s;\n", buf1);
+        }
+        else
+          ferr(po, "missing initial ZF\n");
       }
-      else
-        ferr(po, "missing initial ZF\n");
     }
 
     switch (po->op)
@@ -3881,8 +3960,7 @@ tailcall:
         l = (po->flags & OPF_DF) ? '-' : '+';
         if (po->flags & OPF_REP) {
           fprintf(fout,
-            "  for (; ecx != 0; ecx--, edi %c= %d, esi %c= %d) {\n",
-            l, j, l, j);
+            "  for (; ecx != 0; ecx--) {\n");
           if (pfomask & (1 << PFO_C)) {
             // ugh..
             fprintf(fout,
@@ -3890,10 +3968,12 @@ tailcall:
             pfomask &= ~(1 << PFO_C);
           }
           fprintf(fout,
-            "    if ((cond_z = (%sedi == %sesi)) %s 0)\n",
-              buf1, buf1, (po->flags & OPF_REPZ) ? "==" : "!=");
+            "    cond_z = (%sedi == %sesi); edi %c= %d, esi %c= %d;\n",
+              buf1, buf1, l, j, l, j);
+          fprintf(fout,
+            "    if (cond_z %s 0) break;\n",
+              (po->flags & OPF_REPZ) ? "==" : "!=");
           fprintf(fout,
-            "      break;\n"
             "  }");
           snprintf(g_comment, sizeof(g_comment), "rep%s cmps",
             (po->flags & OPF_REPZ) ? "e" : "ne");
@@ -3917,19 +3997,21 @@ tailcall:
         l = (po->flags & OPF_DF) ? '-' : '+';
         if (po->flags & OPF_REP) {
           fprintf(fout,
-            "  for (; ecx != 0; ecx--, edi %c= %d)\n", l, j);
+            "  for (; ecx != 0; ecx--) {\n");
           fprintf(fout,
-            "    if ((cond_z = (%seax == %sedi)) %s 0)\n",
+            "    cond_z = (%seax == %sedi); edi %c= %d;\n",
               lmod_cast_u(po, po->operand[0].lmod),
-              lmod_cast_u_ptr(po, po->operand[0].lmod),
+              lmod_cast_u_ptr(po, po->operand[0].lmod), l, j);
+          fprintf(fout,
+            "    if (cond_z %s 0) break;\n",
               (po->flags & OPF_REPZ) ? "==" : "!=");
           fprintf(fout,
-            "      break;");
+            "  }");
           snprintf(g_comment, sizeof(g_comment), "rep%s scas",
             (po->flags & OPF_REPZ) ? "e" : "ne");
         }
         else {
-          fprintf(fout, "    cond_z = (%seax = %sedi); edi %c= %d;",
+          fprintf(fout, "  cond_z = (%seax = %sedi); edi %c= %d;",
               lmod_cast_u(po, po->operand[0].lmod),
               lmod_cast_u_ptr(po, po->operand[0].lmod), l, j);
           strcpy(g_comment, "scas");
@@ -4122,6 +4204,18 @@ tailcall:
         delayed_flag_op = NULL;
         break;
 
+      case OP_BSF:
+        assert_operand_cnt(2);
+        out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
+        fprintf(fout, "  %s = %s ? __builtin_ffs(%s) - 1 : 0;",
+          out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]),
+          buf2, buf2);
+        output_std_flags(fout, po, &pfomask, buf1);
+        last_arith_dst = &po->operand[0];
+        delayed_flag_op = NULL;
+        strcat(g_comment, "bsf");
+        break;
+
       case OP_INC:
       case OP_DEC:
         out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
@@ -4299,6 +4393,8 @@ tailcall:
             || (pp->argc_stack > 0
                 && pp->is_stdcall != g_func_pp->is_stdcall))
             ferr(po, "incompatible tailcall\n");
+          if (g_func_pp->has_retreg)
+            ferr(po, "TODO: retreg+tailcall\n");
 
           for (arg = j = 0; arg < pp->argc; arg++) {
             if (arg > 0)
@@ -4332,7 +4428,10 @@ tailcall:
                 pp->arg[arg].type.name);
 
             if (pp->arg[arg].reg != NULL) {
-              fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
+              if (pp->arg[arg].type.is_retreg)
+                fprintf(fout, "&%s", pp->arg[arg].reg);
+              else
+                fprintf(fout, "%s%s", cast, pp->arg[arg].reg);
               continue;
             }
 
@@ -4397,6 +4496,12 @@ tailcall:
       case OP_RET:
         if (g_func_pp->is_vararg)
           fprintf(fout, "  va_end(ap);\n");
+        if (g_func_pp->has_retreg) {
+          for (arg = 0; arg < g_func_pp->argc; arg++)
+            if (g_func_pp->arg[arg].type.is_retreg)
+              fprintf(fout, "  *r_%s = %s;\n",
+                g_func_pp->arg[arg].reg, g_func_pp->arg[arg].reg);
+        }
  
         if (IS(g_func_pp->ret_type.name, "void")) {
           if (i != opcnt - 1 || label_pending)
@@ -4416,17 +4521,20 @@ tailcall:
         break;
 
       case OP_PUSH:
+        out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
         if (po->argnum != 0) {
           // special case - saved func arg
-          out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
           fprintf(fout, "  s_a%d = %s;", po->argnum, buf1);
           break;
         }
         else if (po->flags & OPF_RSAVE) {
-          out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
           fprintf(fout, "  s_%s = %s;", buf1, buf1);
           break;
         }
+        else if (g_func_pp->is_userstack) {
+          fprintf(fout, "  *(--esp) = %s;", buf1);
+          break;
+        }
         if (!(g_ida_func_attr & IDAFA_NORETURN))
           ferr(po, "stray push encountered\n");
         no_output = 1;
@@ -4448,7 +4556,13 @@ tailcall:
               po->operand[0].is_ptr ? "(void *)" : "", 0));
           break;
         }
-        ferr(po, "stray pop encountered\n");
+        else if (g_func_pp->is_userstack) {
+          fprintf(fout, "  %s = *esp++;",
+            out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]));
+          break;
+        }
+        else
+          ferr(po, "stray pop encountered\n");
         break;
 
       case OP_NOP:
@@ -4685,6 +4799,7 @@ int main(int argc, char *argv[])
   int skip_warned = 0;
   int eq_alloc;
   int verbose = 0;
+  int multi_seg = 0;
   int arg_out;
   int arg;
   int pi = 0;
@@ -4698,12 +4813,14 @@ int main(int argc, char *argv[])
       verbose = 1;
     else if (IS(argv[arg], "-rf"))
       g_allow_regfunc = 1;
+    else if (IS(argv[arg], "-m"))
+      multi_seg = 1;
     else
       break;
   }
 
   if (argc < arg + 3) {
-    printf("usage:\n%s [-v] [-rf] <.c> <.asm> <hdrf> [rlist]*\n",
+    printf("usage:\n%s [-v] [-rf] [-m] <.c> <.asm> <hdrf> [rlist]*\n",
       argv[0]);
     return 1;
   }
@@ -5079,8 +5196,23 @@ do_pending_endp:
       continue;
     }
 
-    if (wordc == 2 && IS(words[1], "ends"))
-      break;
+    if (wordc == 2 && IS(words[1], "ends")) {
+      if (!multi_seg)
+        break;
+
+      // scan for next text segment
+      while (fgets(line, sizeof(line), fasm)) {
+        asmln++;
+        p = sskip(line);
+        if (*p == 0 || *p == ';')
+          continue;
+
+        if (strstr(p, "segment para public 'CODE' use32"))
+          break;
+      }
+
+      continue;
+    }
 
     p = strchr(words[0], ':');
     if (p != NULL) {