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

catfood@NCoast.ORG (Mark W. Schumann) (10/27/90)

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.

Sigh...I think you may have to rewrite.  Sorry.



-- 
============================================================
Mark W. Schumann  3111 Mapledale Avenue, Cleveland 44109 USA
Domain: catfood@ncoast.org
UseNet: ...usenet.ins.cwru.edu!ncoast!catfood
============================================================

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

rhys@batserver.cs.uq.oz.au (Rhys Weatherley) (10/31/90)

dfoster@jarthur.Claremont.EDU (Derek R. Foster) writes:

>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?  [ ... etc ... ]

>Although this is generally true in "nice" programs, there may be a somewhat
>kludgy workaround for this.
>	[ ... code to swap stacks, and do other ugly things ... ]

EEK!!  This solution should be applied with "fingers crossed"!  I won't
go into the code.  Answering the original question, looking at lots of
Unix programs, you find that many of them allocate big buffers and things
on the stack.  For example:

	char	buffer[BUFSIZ];

This is "plenty fine" on Unix boxes where most everything is 32 bit, with
HUGE stacks.  However on MS-DOS machines, you of course (as pointed out
earlier) only have 64K of stack, at best.  However, just change the
above line to:

	static	char	buffer[BUFSIZ];

And "hey presto", you've decreased the stack usage in a big way, by
putting the buffer into the global data space.  Also, with recursive 
functions (as usually present in parsers), the buffers soon mount up.  
Using this (which authors should do anyway for portability) is 
much "cleaner" than swapping stacks and stuff.  There is one caveat: 
you must be aware that you DON'T have a new copy of the buffer each time 
in a recursive routine (it's always the same one).  Careful inspection 
of the code can help you out greatly.

This is not a "total" solution, but preferred to stack swaps wherever
possible.

Rhys.

P.S. Derek, you changed _SS, but forgot about _SP.  Weird things could happen!

+===============================+==============================+
||  Rhys Weatherley             |  University of Queensland,  ||
||  rhys@batserver.cs.uq.oz.au  |  Australia.  G'day!!        ||
+===============================+==============================+

cage@fmeed1.UUCP (Russ Cage) (10/31/90)

I tackled the porting of some MIT music synthesis code from
VAX over to MSC on a PC/AT a few years ago.  The biggest
problem was (deja vu?) VAX-hackers allocating enormous arrays
as automatics.  (A pair of 10,000 element float arrays, for
example...)  Anyway, Craig Harris wanted it done, so I did it.

MSC requires HUGE pointers and use of halloc() (if memory
serves, it has been a while) to reference objects that big.
But the code can be re-written with a bit of care and it
can be made to work.

The second biggest hassle was the penchant of VAXhackers
for de-referencing NULL.  Aside from whupping them upside
the head with an ASR-33, I have no solution for that.
-- 
Russ Cage	Ford Powertrain Engineering Development Department
Work:  itivax.iti.org!cfctech!fmeed1!cage   (CHATTY MAIL NOT ANSWERED HERE)
Home:  russ@m-net.ann-arbor.mi.us  (All non-business mail)
Member:  HASA, "S" division.

lsalomo@hubcap.clemson.edu (lsalomo) (11/01/90)

If you extend the switching stacks idea a little further, you can implement
a multi-tasking OS kernal rather easily.

Do not try this at home:   ;)

Intercept the timer interrupt to count the number of time clicks the current
process has been running for.  When it reaches a terminal count, generate
an interrupt to call the dispatcher.

The dispatcher's job is to remove the process in the run queue and place
it in ascending order (by priority) in the ready queue.  It then takes the
first item from the ready queue (==the highest priority) and places it in the
run queue.  THEN (here's the tricky part) it switches stack segments from the
one which belonged to the last running process to the stack segment of the
next process to run.

When all of the interrupts unwind, the point in the new process where it
was interrupt gets popped off into the registers, and the new process starts
executing where it left off.

= = = = = = = = 
Off course, the only tricky part is loading the .EXE's from disk and 
initializing the stacks to point to their entry point.

= = = = = = = =
If you'd like to discuss this further, contact me offline...

Cheers,
Q - the "Q"uestor for knowledge (, a degree, etc.)

lsalomo@hubcap.clemson.edu
ibmman@prism.clemson.edu
ibmman@clemson.clemson.edu
=============================================================================
"Gee Wally, I think there's something wrong with the Beaver."
=============================================================================

stever@Octopus.COM (Steve Resnick ) (11/01/90)

In article <9449@jarthur.Claremont.EDU> dfoster@jarthur.Claremont.EDU (Derek R. Foster) writes:

[Stuff deleted]
>
>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)];
>
Warning Will Robinson! :) There is nothing to prevent you from changing the
stack on a '86 processor, but you *cannot* access more than 64K. Even
though TC supports huge pointers and deals with the additional segment
arithmetic, the processor doesn't. Eg, PUSH/POP instructions will fail
on a segment wraparound. 

If you do change the stack segment and pointer to a new address you cannot
do any stack checking since the stack segement is different than the one
defined by the C runtime startup routines.

>/* 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;
	
	You *MUST* disable interrupts during this operation. If an interrupt
	occures during the stack segment transition, you will not return
	from the interrupt correctly and crash the machine.


>  _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.
>

Hope this helps.....


Steve

-- 
----------------------------------------------------------------------------
steve.resnick@f105.n143.z1.FIDONET.ORG - or - apple!camphq!105!steve.resnick
Flames, grammar errors, spelling errrors >/dev/nul
The Asylum OS/2 BBS - (408)263-8017 IFNA 1:143/105.0

dfoster@jarthur.Claremont.EDU (Derek R. Foster) (11/01/90)

In article <5497@uqcspe.cs.uq.oz.au> rhys@batserver.cs.uq.oz.au writes:
>dfoster@jarthur.Claremont.EDU (Derek R. Foster) writes:
>>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?  [ ... etc ... ]
>
>>Although this is generally true in "nice" programs, there may be a somewhat
>>kludgy workaround for this.
>>	[ ... code to swap stacks, and do other ugly things ... ]
>
>EEK!!  This solution should be applied with "fingers crossed"!

Yes, definitely!

>[mention of using static buffers instead of automatic buffers when possible]
>
>This is not a "total" solution, but preferred to stack swaps wherever
>possible.
>
>Rhys.
>
>P.S. Derek, you changed _SS, but forgot about _SP.  Weird things could happen!

Quite true. I didn't realize that I had forgotten to set _SP in my code.
I might even venture to say that weird things WOULD happen. (especially
since I set _SS to where _SP was supposed to go...I must have been 
half-asleep that night! As written, the stack tromps all over non-buffer 
memory!) But then I did say that the code I presented almost certainly
wouldn't work :-) My code was mostly presented as a general sketch of what
could be done, not how precisely to do it. I'd rather leave that part
to someone who's done it before. 

Anyway, your method is certainly preferable to mine provided the savings
is adequate. I certainly don't endorse stack swaps. I just think that as
an emergency alternative to rewriting a large program, they have their
place, ugly / nonportable though they are. I would avoid them whenever
humanly possible, but it's a good idea to know that they are possible for
those rare moments when a kludge is desperately needed. 

>+===============================+==============================+
>||  Rhys Weatherley             |  University of Queensland,  ||
>||  rhys@batserver.cs.uq.oz.au  |  Australia.  G'day!!        ||
>+===============================+==============================+

Derek Riippa Foster

dfoster@jarthur.Claremont.EDU (Derek R. Foster) (11/01/90)

In article <1990Oct31.190520.11526@Octopus.COM> stever@octopus.UUCP (Steve Resnick ) writes:
>In article <9449@jarthur.Claremont.EDU> dfoster@jarthur.Claremont.EDU (Derek R. Foster) writes:
>
>[Stuff deleted]
>>
>>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)];
>>
>Warning Will Robinson! :) There is nothing to prevent you from changing the
>stack on a '86 processor, but you *cannot* access more than 64K. Even
>though TC supports huge pointers and deals with the additional segment
>arithmetic, the processor doesn't. Eg, PUSH/POP instructions will fail
>on a segment wraparound. 

Although this is technically true, the way you
have phrased it is somewhat misleading. The limit is 64k PER STACK
SEGMENT. The technique I am using first fills up the "default"
stack segment, <= 64k, then switches to a new one when that one is
full, also <= 64k, for a resultant "effective" stack size of >64k.
So even though the stack pointer (which, as another poster pointed out,
I forgot to mention in my sample code) can never be farther away from
_SS than 64k, we could build an indefinitely long "linked list" of 
stacks, just as long as we remember to switch from one to the other
when we are in danger of over/underflowing the one we are in. We should
never have to deal with PUSHes and POPs over a segment boundary during
this process, since if the stack pointer gets close to one, we're
supposed to switch to a new stack.

>
>If you do change the stack segment and pointer to a new address you cannot
>do any stack checking since the stack segement is different than the one
>defined by the C runtime startup routines.
>

Not necessarily...

I think it is _highly_ likely that _stklen measures the height of the
stack relative to the stack segment register, and not to some absolute
position in memory. (After all, what would be the point of using
the segment value of a quantity if it's never supposed to change, and
you always have a (normally constant) register pointing at it?)
As such, if each "link" in the "linked list" is of
a size to accomodate _stklen, then if we overflow the current
stack segment by exceeding _stklen, a "stack overflow" message is
printed, but if we instead switch to a new stack and reset the
stack pointer, we can keep adding to the new stack until we are
again in danger of exceeding _stklen relative to the _new_ stack segment.
Thus the stack checking routines perform their intended purpose, sort of,
namely detecting a program trying to write stack data outside of any
stack segment.

>>  oldSS = _SS;
>	
>	You *MUST* disable interrupts during this operation. If an interrupt
>	occures during the stack segment transition, you will not return
>	from the interrupt correctly and crash the machine.

True. Definitely disable interrupts.

Oh, incidentally, another bug in my example code: don't store the
old _SS in an automatic (local) variable. Since this variable actually
resides on the first stack, once you change it, you have no way to
access the old value. Use a static variable instead. In fact, remember
that changing _SS and/or _SP will make a function "forget" ALL its
local variables' values until the old values of _SS and _SP are
restored. Perhaps a better way to do my second example would be to
have a function which has no other purpose than to swap the stacks,
call other functions, then swap the stacks back. The calling
routine would then decide whether to call the other functions directly,
or to make a call through the swapping function. (The decision being made
by determining how much stack space is left in the current segment.) 
This way you wouldn't have to worry about losing the values of
various local variables over the course of a stack swap.

Also, for the same reason, don't even THINK of trying this technique
in a memory model with near pointers. You won't be able to refer to
local variables of previously-called functions by address if you do,
since they may be in a different segment... Bad Things would happen.

>steve.resnick@f105.n143.z1.FIDONET.ORG - or - apple!camphq!105!steve.resnick
>Flames, grammar errors, spelling errrors >/dev/nul
>The Asylum OS/2 BBS - (408)263-8017 IFNA 1:143/105.0

I hope this clarifies things a little. Thanks for the input.

Derek Riippa Foster

cage@fmeed1.UUCP (Russ Cage) (11/02/90)

In article <9478@jarthur.Claremont.EDU> dfoster@jarthur.Claremont.EDU (Derek R. Foster) writes:
>.... The technique I am using first fills up the "default"
>stack segment, <= 64k, then switches to a new one when that one is
>full, also <= 64k, for a resultant "effective" stack size of >64k.

There are two serious problems with this.

1.)	Unless all pointers are 'far', automatic variables
	from one function will not be accessible to functions
	past the stack-overflow mark; they will be in a
	different segment.

2.)	Unless careful copying of data from stack frame to
	stack frame is done on stack overflow, parameters to
	the called function could get lost.  Worse, the
	size of the parameter list in C is not known to
	functions such as printf()!

In my experience, it is easier to re-write the code to remove
the features which require excessive stack space.
-- 
Russ Cage	Ford Powertrain Engineering Development Department
Work:  itivax.iti.org!cfctech!fmeed1!cage   (CHATTY MAIL NOT ANSWERED HERE)
Home:  russ@m-net.ann-arbor.mi.us  (All non-business mail)
Member:  HASA, "S" division.

REIDMP@MAINE.BITNET (11/03/90)

If you have lots of memory to play with for code space, one really
simple solution to try is to make as many of the functions inline
as possible.  Another simple fix that might work with TC (I can't
recall for sure how the compiler treats variable declarations, but
this works on many ANSI C compilers), is to delay declarations until
the block you will use the variable in.  Example:
     Before:                     After:
     void foo(char * s) {        void foo(char * s) {
     int i;                      if (s) {
     if (s) {                      int i;
       i = 1;                      i = 1;
     }                           }
     ...                         ...
     }                           }
 
The example is very simple and brain-dead, but the point is this: since
variables reside (mostly) on the stack, and (generally) live until the
end of the block of their declaration, push them further down into the
nested blocks, particularly if the variables sometimes won't be used.
This way you shorten the average life of data on the stack and sometimes
avoid unused variables ever being created.  With deeply nested recursion
this can make a difference.  However, this will only work if the compiler
doesn't optimize out the declaration to do one single stack adjustment
when the function is entered.
 
ciao for now
   Reid

bcw@rti.rti.org (Bruce Wright) (11/11/90)

In article <1990Oct31.190520.11526@Octopus.COM>, stever@Octopus.COM (Steve Resnick ) writes:
> In article <9449@jarthur.Claremont.EDU> dfoster@jarthur.Claremont.EDU (Derek R. Foster) writes:
> 
> >void StackSwapFunction(void)
> >{
> >  unsigned oldSS;
> >
> >  oldSS = _SS;
> 	
> 	You *MUST* disable interrupts during this operation. If an interrupt
> 	occures during the stack segment transition, you will not return
> 	from the interrupt correctly and crash the machine.
> 
> 
> >  _SS = FP_SEG(ABuffer+sizeof(ABuffer));

This is misleading.  It's not necessary to disable interrupts when
swapping stack segments if you do it right!  The point is that the
80x86 processors disable interrupts for one instruction after loading
the SS register (some of them disable interrupts for one instruction
after loading ANY segment register, but later processors limited it
to SS - I forget the exact details of which processors behave which
way because it's usually not important).  So if the next instruction
IMMEDIATELY after loading the new value into SS loads the new value
into SP, then you're golden and don't need to mess around with manually
disabling interrupts.  Look in any of the processor handbooks for the
Intel processors.  I don't know enough about Turbo C++ to know whether 
it gets in your way and inserts other instructions after the assignment
to SS, but it does work fine in assembler.

On the subject of swapping the stack, another place that it's often
needed is for interrupt service routines running in real mode - in
this mode the 80x86 processor does not perform the interrupt on a
separate stack but on whatever stack the application program is using.
An amazing number of application programs have allocated EXACTLY the
amount of stack that they are going to be using, and there isn't
enough room to even save the registers for the interrupt service
routine.  (Note that they may have taken into account the amount of
stack space required by any BIOS calls that they make, so that your
interrupt may be OK until you interrupt a BIOS call).  Anyway, if you
are writing a real mode interrupt handler it's a real good idea to 
swap stacks to a local stack while using as little of the application
program's stack as possible;  it can be useful to know that when
swapping in either direction you don't have to worry about whether
you have 80x86 interrupts enabled when you move the segment as long
as you do things in the proper order.

						Bruce C. Wright

hill@netcom.UUCP (Tom Hill) (11/11/90)

In article <1990Nov10.223333.4628@rti.rti.org> bcw@rti.rti.org (Bruce Wright) writes:
...
>> >  oldSS = _SS;
>> 	
>> 	You *MUST* disable interrupts during this operation. If an interrupt
>> 	occures during the stack segment transition, you will not return
>> 	from the interrupt correctly and crash the machine.
>
>This is misleading.  It's not necessary to disable interrupts when
>swapping stack segments if you do it right!  The point is that the
>80x86 processors disable interrupts for one instruction after loading
>the SS register (some of them disable interrupts for one instruction
>after loading ANY segment register, but later processors limited it
>to SS - I forget the exact details of which processors behave which
>way because it's usually not important).  So if the next instruction
>IMMEDIATELY after loading the new value into SS loads the new value
>into SP, then you're golden and don't need to mess around with manually
>disabling interrupts.

Sorry, but this is only correct for the later intel processors. The original
8088s did not disable the interrupts after a load of SS, thus requiring
thta interrupts be disabled. If you know your code will only run on
ATs & up, sequential loads of SS & SP work fine. If you want to be sure
your code will run on your occasional original PC, disable the interrupts.


Tom Hill