Parameter Passing in Procedures


Techniques in Parameter Passing

When writing procedures it is usually necessary to pass information to the procedure to customize its operation. In high-level languages this information is in the form of parameters that are included as part of the procedure call. In assembler, there are various techniques to pass information.

The simplest technique is to simply store the information in various registers. IOASM uses this technique, where the information is stored in the AX register. The advantage of using this technique is that information in the registers can be immediately used, and the code is simpler and faster. The disadvantage is that the registers restrict the number of parameters and they become tied up once a value is stored in a particular register. Also, once a procedure is written to use a particular register, every calling routine must set the parameter in that same register, irrespective of whether it is in use or not.

The more general technique is to use the stack to store parameters. The advantage is that there are no restrictions to size or register usage. The disadvantage is that the code becomes a bit more complex.

Parameter-Passing using the Stack

Before calling a procedure, all its parameters must be stored on the stack.

push ax
push bx
call testproc

The procedure can now access these variables on the stack. Recall that the stack pointer (SP - register) points to the topmost element on the stack. After calling a procedure, this is normally the return address. Thus, the first parameter (or rather, the last parameter pushed) is at SP+2. To access this parameter (value of BX in the above example), the address [SP+2] can be used. This follows the conventions of addressing as discussed in chapter 2, so is perfectly legal. The second parameter (AX) can be accessed using the address [SP+4].

The only problem with this approach is that the SP register's value will change if the stack is used within the procedure. Thus it may be better to note the value at the start of the program and use this value for parameter references. The value can be stored in the BP (base pointer) register. Thus references can be made relative to the BP register, viz. [BP+2] and [BP+4]. The code for the procedure will therefore look like:

testproc PROC
mov bp,sp
...
mov sp,bp
ret
testproc ENDP

The first MOV statement sets up the BP register with the initial value of the SP register. The last statement in the procedure will restore the stack if it wasn't cleared up properly within the procedural code.

This is still fallible because the BP register could be storing an important value. Suppose that one procedure calls another procedure. It would be quite disastrous because the second procedure would destroy the BP register and the first procedure would not be able to regain its value. It makes a lot of sense to save the BP register before calling a procedure and restore it afterwards. But this would mean two extra lines of code for every procedure call. Instead, its better to save the BP register at the top of the procedure code and restore it at the bottom.

testproc PROC
push bp
mov bp,sp
...
mov sp,bp
pop bp
ret
testproc ENDP

Of course, since there is now one extra element on the stack, all parameter references must be shifted by one position. Thus, the value of BX (as per the example) is now at [BP+4] and AX is at [BP+6]. Any number of variables can be stored on the stack in a similar manner. For all cases the last word will be located at [BP+4], the previous one at [BP+6], etc..

After calling the procedure, it is also necessary to clear the parameters off the stack. Instead of putting in a number of POP statements after every procedure call, the RET instruction has a mechanism to automatically kill the parameters after returning to the calling code. If a single constant integer parameter is specified, then those number of bytes will be popped off the stack when the procedure returns. In the example above, there are two word parameters which take up four bytes, so the last instruction in the procedure would be

ret 4

Sample Program #9

; selection sort
DOSSEG
.MODEL SMALL
.STACK 4096

.DATA
anArray dw 100 dup (0) ; data
N dw ? ; size of array
Message1 db 'Enter the size of the list : $'
Message2 db 'Enter number : $'
Message3 db 'The sorted list is : ',13,10,'$'

.CODE

EXTRN readsint:proc,writesint:proc
; use procedures from IOASM

ProgramStart:
mov ax,SEG _DATA ; set up data segment
mov ds,ax

lea dx,Message1 ; output message
mov ah,9
int 21h
call readsint ; get N
mov N,ax

lea ax,anArray ; push address of array onto stack
push ax
push N ; push size of array onto stack
call inputarray ; read in array

lea ax,anArray ; push address of array onto stack
push ax
push N ; push size of array onto stack
call selectionsort ; sort array

lea dx,Message3 ; output message
mov ah,9
int 21h
lea ax,anArray ; push address of array onto stack
push ax
push N ; push size of array onto stack
call outputarray ; output array

mov ah,4ch ; terminate program
int 21h

inputarray PROC ; procedure to input an array
push bp ; save stack position
mov bp,sp

xor si,si ; initialize array index
mov cx,[bp+4] ; get size of array
mov bx,[bp+6] ; get start address
cmp cx,0 ; check for null-array
je inputend
inputstart: ; loop to input array
call readsint ; get integer
mov [bx+si],ax ; store in array
add si,2 ; increment array index
loop inputstart

inputend:
mov sp,bp ; restore stack
pop bp
ret 4 ; pop parameters off stack and return
inputarray ENDP

outputarray PROC ; procedure to output an array
push bp ; save stack position
mov bp,sp

xor si,si ; initialize array index
mov cx,[bp+4] ; get array size
mov bx,[bp+6] ; get start address
cmp cx,0 ; check for null-array
je outputend
outputstart: ; loop to output array
mov ax,[bx+si] ; get array element
call writesint ; output integer
add si,2 ; increment array index
loop outputstart

outputend:
mov sp,bp ; restore stack
pop bp
ret 4 ; pop parameters off stack and return
outputarray ENDP

selectionsort PROC ; procedure to selection sort an array
push bp ; save stack position
mov bp,sp

mov cx,[bp+4] ; get array size
mov bx,[bp+6] ; get start address

selectionstart: ; loop to get smallest element to front
cmp cx,1 ; check if array length > 1
jbe selectionend ; if true, goto end

mov si,2 ; initialize index to point to second element
mov dx,2 ; initialize position in array to second element
mov ax,[bx] ; get first element

selectioncheck: ; loop to check all elements are bigger than first
cmp ax,[bx+si] ; compare i'th element with first element
ja selectionswap ; swap if necessary
jmp selectionnextnum ; otherwise just goto next element

selectionswap: ; swap routine
push ax ; swap ax and [bx+si] using the stack
push [bx+si]
pop ax
pop [bx+si]

selectionnextnum: ; goto next number in array
add si,2 ; increment index
cmp dx,cx ; check if last element was reached
je selectionnextrun ; if true, return to outer loop
inc dx ; otherwise, increment position in array
jmp selectioncheck ; and check next element

selectionnextrun:
mov [bx],ax ; save ax into first element
dec cx ; shrink array length by 1
add bx,2 ; drop first element
jmp selectionstart ; apply sort to rest of array

selectionend:
mov sp,bp ; restore stack
pop bp
ret 4 ; pop parameters off stack and return
selectionsort ENDP

END ProgramStart