[comp.sys.ibm.pc] MSC 5.0 Local Stacks -- HERE IS HOW TO DO IT!

jaynec@homxc.UUCP (J.KING) (05/04/88)

In article <539@siemens.UUCP>, jrv@siemens.UUCP (James R Vallino) writes:
> In article <2045@optilink.UUCP> elliott@optilink.UUCP (Paul Elliott x225) writes:
> >I am doing some TSR stuff using Microsoft C 5.0, and would like to
> >get access to the SS and SP registers (to set up a local stack).
> >TurboC has psuedo-variables for all the registers, and it is pretty
> >simple to read and write to these and do what I want (in TutboC).
> 
> I had the same wish and could not find a way to set up a local stack short
> of some assembly code. I punted and took the risk of using whatever stack
> is active when the TSR is entered. So far that has worked but I would be
> wary of not putting in the stack swap for a general use TSR.
> 

That's a bad risk in my experience.

I have successfully managed stack swapping -- you are right, assembler is
the only way.  At the end of this I will include the source code, NO
GUARENTEES -- this works for me, but it's by no means foolproof.

You'd better swap stacks or you might get in real deep stuff.  You should
realize that if you don't swap stacks, you should compile so that the code
knows that DS<>SS when its compiling your routine.  This is accomplished
with a -Aw (FLAME ON:  WHY DOES MICROSOFT INSIST ON CASE SENSITIVE COMMAND
LINE OPTIONS.  FLAME OFF -- I feel better ), as in "cl -Od -AL -Aw foo.c". 
Without this, I had several bizarre problems like code hanging on the
statement "x = y".   

Even with this option, however, you must realize that almost all of the C
runtime libraries are compiled to work only if DS=SS (I had originally
written iff but MS bugs made me change the wording), and you can't really
be sure whether or not the routine you want will cause a bizarre hangup. 
For example, ctime() uses the stack partially -- in my example, I simply
printed the current time, and it came out as partially correct, and
partially greek letters -- the stack had been used for some of the date
stamp instead of the data segment (that sort of stuff makes me really
nervous).   

Of course, you can simply ignore all of the C runtime routines -- in a TSR
you may have to anyway because of re-entrency (sp?) problems, but if you
have to give up the runtime stuff you might as well use assembler anyway. 
(Spoken like a true ASM hacker).  

Finally, even after you swap stacks, you will (as I did) run into a real
pain -- stack overflow messages that are generated because of the stack
swapping and not by any real stack overflows.  My response to this was to
turn off all stack checking (compile with -Ox, equivalent to living
dangerously).  BUT SURPRISE, runtime library routines check the stack
anyway and kick you out.

At this point, I called MS and (HUZZAHS ON ------

=============================================================
        MICROSOFT PRODUCT SUPPORT ANSWERED MY QUESTION!!!
=============================================================

HUZZAHS OFF -- its the first time they've really answered a question
successfully and I am grateful).  They referred my to the assembler stack
checking routine (which I'd forgotten was supplied on the UTILITIES disk),
which allows me to turn optimization back off, and get around the error
checks.  


Here are the two routines for stack swapping -- These assume large model
compiles and provide a stack that is accessible only to the assembler
routine.  The stack size is provided by the user.  Call StackADJ() to swap
stacks, and StackRES() to restore original stack.  I call these from within
my C interrupt handler routine (which has already changed DS to the
"correct" value).

Redirect any flames about these routines to /dev/null.

James H. King
AT&T Bell Laboratories
HO 3K302
homxc!jaynec
201 949 8908

;	Static Name Aliases
;
;	extrn	_CurrentSS:word
;	extrn	_CurrentSP:word
	extrn	STKHQQ:word 		; stack bottom

STACK_TEXT	SEGMENT  WORD PUBLIC 'CODE'
STACK_TEXT	ENDS

_DATA	SEGMENT  WORD PUBLIC 'DATA'
LocalStack	db 	4024d dup ("stack   ")
StackTop	db	0
Orig_SS		dw	0
Orig_SP		dw	0
Orig_BP		dw	0
Orig_DI		dw	0
Orig_SI		dw	0
OrigSTKHQQ	dw	0
_DATA	ENDS

CONST	SEGMENT  WORD PUBLIC 'CONST'
CONST	ENDS
_BSS	SEGMENT  WORD PUBLIC 'BSS'
_BSS	ENDS
DGROUP	GROUP	CONST, _BSS, _DATA
	ASSUME  CS: STACK_TEXT, DS: DGROUP, SS: DGROUP
STACK_TEXT      SEGMENT

	ASSUME	CS: STACK_TEXT
	PUBLIC	_StackADJ
	PUBLIC	_StackRES

_StackADJ	PROC FAR
	mov	ax,ss
	mov	Orig_SS,ax
	mov	ax,sp
	mov	Orig_SP,ax
	mov	ax,bp
	mov	Orig_BP,ax
	mov	ax,di
	mov	Orig_DI,ax
	mov	ax,si
	mov	Orig_SI,ax
	mov	bp,sp
	mov	ax,STKHQQ
	mov	OrigSTKHQQ,ax
	mov	cx,ss:[bp]		;load return address offset
	mov	dx,ss:[bp+2]		;load return address segment
	mov	ax,ds
	mov	ss,ax
	mov	ax,offset LocalStack+4024d	;load top of local stack
	mov	sp,ax				;reset stack pointer
	mov	ax,offset LocalStack
	mov	[STKHQQ],ax
	push	dx			;push return segment to new stack
	push	cx			;push return offset to new stack
	mov	bp,sp
	ret
_StackADJ	ENDP

_StackRES	PROC FAR
	mov	bp,sp
	mov	cx,ss:[bp]		;load return address offset
	mov	dx,ss:[bp+2]		;load return address segment
	mov	ax,Orig_SS
	mov	ss,ax
	mov	ax,Orig_SP
	mov	sp,ax
	mov	ax,Orig_BP
	mov	bp,ax
	mov	ax,Orig_SI
	mov	si,ax
	mov	ax,Orig_DI
	mov	di,ax
	mov	ax,OrigSTKHQQ
	mov	STKHQQ,ax
	push	dx			;push return segment to new stack
	push	cx			;push return offset to new stack
	ret
_StackRES	ENDP

STACK_TEXT	ENDS
END