Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations IamaSherpa on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

Simple problem: using stack to save regs, pass params, & return values

Status
Not open for further replies.

TheInsider

Programmer
Jul 17, 2000
796
CA
Hello,

I'm using ArrowSoft's 8086 Assembly compiler/linker. To avoid pasting oodles of code, I have written a simplified version of the code, below:

Code:
main proc
	... 			;do something
	call function1
	... 			;do something
main endp

function1 proc
	mov cx, 0Ah 		;setup counter for loop of 10

	label1:
		push cx		;function2 uses cx as well, so save current value onto stack

		mov bx, 01h	;setup argument for function 2
		push bx		;push argument onto stack
		call function2

		pop cx		;restore original value of cx from stack, so counter is correct
	loop label1

	ret
function1 endp

;------------------------------------------------------------------------------------------------

function2 proc
	pop bx			;pop parameter value off of the stack
	mov cx, 05h		;setup counter for loop of 5

	label2:
		push cx		;function3 uses cx as well, so save current value onto stack

		... 		;do something with bx

		mov cx, 0FFh	;setup argument for function 3
		push cx		;push argument onto stack
		call function3

		pop cx		;function3 placed a return value in cx
		...		;do something with cx

		pop cx		;restore original value of cx from stack, so counter is correct
	loop label2

	ret
function2 endp

;------------------------------------------------------------------------------------------------

function3 proc
	pop cx			;pop parameter value off of the stack (used for loop counter)

	label3:
		...		;do something
	loop label3		;loop as many times as parameter value requested

	...			;do something with cx
	push cx			;push value of cx onto stack for return value

	ret
function3 endp

In summary, I have 3 PROCs. Each procedure uses CX to LOOP. As you can see, this requires that CX be stored [on the stack] before calling each procedure -- in order to ensure that CX will ultimately retain the correct value within each procedure.

At the same time I am trying to pass parameters by pushing them on the stack and popping them off the stack. I also wish to place return values from procedures onto the stack, rather than passing and returning via registers.

The problem is that, although I am pushing and popping in the correct order, CALL and RET are placing the IP on the stack and it is causing chaos! The program crashes because the wrong values are getting pushed and popped.

How can I modify this so that I can save CX each time, pass parameters via the stack, and return values via the stack?

Thanks
 
Yes that happens when you use call and rets, that is to preserve the work environment. If you want to avoid that use regular jumps,

regards,

Rick
 
Thank you for your reply. Yes, I could use regular jumps; however, I would still need to use a register to hold some sort of counter.

In my "real" procedures, I am already using all of the registers for various things. So, in any case, I would still want to save and restore at least some of these registers onto the stack between procedure calls.
 
What you're trying to do is just what C and Pascal and almost any other high-level language do, and of course it's a perfectly acceptable way to work. I'm biassed towards pascal for no good reason, so I'll explain that. C is a bit different but the same sort of idea.

Pascal pushes its parameters, and then calls. The stack therefore contains the return address followed by parameters.

On the start of a procedure, the stack pointer sp is stored in the bp register, but only after the old bp register has been saved:
push bp
mov bp, sp
Then, if local variables are necessary, space is reserved on the stack for them, by subtracting from the sp:
sub sp, 8
The stack structure is now 8 empty bytes, followed by the bp, the return address, and the parameters.

During the procedure, the parameters and local variables are read relative to bp, which you may remember points to where sp was at the start of the procedure. It's a simple matter, if you draw a picture of the stack, to work out the offsets. Code ends up looking like:
mov ax, [bp+8]
add [bp-4], ax
(these are in the stack segment by default. That's the rule for bp!)

If you want to use registers in the procedure, they must be initialised in the procedure. Pascal doesn't preserve them on return from procedures. Therefore if you're doing a loop counted by cx and you call a procedure, you need to save cx first:
mov cx, 27
label: do something here
push cx
call aprocedure
pop cx
dec cx
jnz label
carry on here

At the end of the procedure, pascal restores sp from bp (thereby having the nice side-effect of cancelling out any mess you've made with pushing and popping....), pops bp and returns. After restoring sp, bp is in the right place to be popped. Popping bp is important because it was used for addressing the local variables and parameters of the calling procedure (the parent procedure)! After popping bp, the return address is present. And fortunately the ret instruction can be followed by a number of bytes to add to sp after taking the address, which can be used to mop up the original pushed parameters, so they need never be popped. e.g.
mov sp, bp
pop bp
ret 12
Note that the "enter" and "leave" instructions do the stack-frame construction in the same way as the sp/bp instructions given here.

Hope that helps a bit.
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top