[alt.msdos.programmer] Porting UNIX apps. to MS-DOS

dfoster@jarthur.Claremont.EDU (Derek R. Foster) (10/31/90)

In article <1990Oct26.223541.26634@NCoast.ORG> catfood@NCoast.ORG (Mark W. Schumann) writes:
>DLV101@psuvm.psu.edu (Dwaine VanBibber) writes:
>>Does anyone have some general rules for porting C programs developed under
>>UNIX to MS-DOS? I've developed a small language interpreter that I would
>>like to be able to run under DOS. I have already attempted this, and it
>>compiles fine using TC++, however I keep getting stack overflow. I am
>>using the large memory model. Setting TC++'s  _stklen variable to something
>>large doesn't seem to help any either. I should also point out that the
>>program contains a recursive descent parser and function calls are often
>>nested quite deep. Compiling with TC 2.01 yields the same results. Do I
>>have to do a complete rewrite (ie. eliminate deep function calls or resort
>>to bottom-up parsing), or can it be coerced to work? Any comments or
>>volunteers to examine code? Thanks in advance.
>>--Dwaine
>
>You have a problem with the Intel '86 architecture.  No matter what you
>do in C on an '86 machine, the stack segment cannot exceed 64K.  That's
>because the SS register is (wierd exceptions aside) held constant through
>execution of a program.  Since an Intel offset is a 16-bit pointer, you
>are limited to the 64K.

Although this is generally true in "nice" programs, there may be a somewhat
kludgy workaround for this. DISCLAIMER: I haven't tried the following
suggestion. This is all theory on my part, and I would be interested in
hearing comments on it. Also the code I present is sketchy at best, and
is probably outright wrong in some respects. Don't expect it to work;
just use it as a model.

Anyway, you might try changing the stack segment pointer to point into
a new segment of your own when the old one gets filled. For instance,
if you can identify a particular part of the program which will be
called only when the stack is almost filled, you might try something
like ...

void huge char ABuffer[(some fancy calculation probably involving _stklen
  to preserve the ability to detect stack overflow) + 16 (the 16 is to
  ensure that I can always get a buffer of at least the size I want 
  which starts on an even paragraph boundary)];

/* note that ABuffer is not meant to be a huge pointer to an array of
   chars, but a pointer to an array of 'huge chars'. This means it will
   be allocated its own data segment. I *THINK* this is the right syntax...*/

void StackSwapFunction(void)
{
  unsigned oldSS;

  oldSS = _SS;
  _SS = FP_SEG(ABuffer+sizeof(ABuffer));

  Do other function calls here.... possibly if they get too deep you could
  even do this same strategy with another buffer. Might even consider
  dynamic allocation of such buffers. Just remember that the stack segment
  has to start on an address that's a multiple of 16...

  _SS = oldSS;
}

All functions called by this function will put their return 
addresses, local variables, etc. in the new stack space, which is in the
buffer. You may have other housekeeping tasks to do as well, possibly
involving changing some global variables that are used internally by
TC to detect end-of-stack. (If stack checking is turned on.) In fact, 
you would have to do quite a bit of twaddling to make this work (This
is written off of the top of my head), but I bet it would be easier than
rewriting the whole of a recursive-descent parser.

In fact, a possibly more useful way to do this for your particular problem
might involve keeping track of how much
space is being used in the stack (via _SS and _stklen?) and then modify
part of your parser's mutual recursion to work like:

void RecursiveFunction(some parameters)
{
  int stacksaved;

    if (very little stack space left)
    {
      save old _SS, point _SS at the _end_ of some (unused!) buffer.
      stacksaved = 1;
    }

    do whatever this function is supposed to do, possibly involving
    recursive calls.

   if (stacksaved)    /* This part must happen before *EVERY* return */
     restore old _SS; /* from this function. You can't return from this */
   return;            /* function without having done this!!! */
}

Anyway, I hope this helps. If you actually succeed in getting this to
work, I'd be interested in seeing the relevant chunks of your source
code, so that I know the _exact_ way to do it instead of vague
generalities like those above.

Good luck!

Derek Riippa Foster