Our Blog

Передача параметров через стек

При написании программ на языке ассемблера часто приходится разбивать свой код на несколько подпрограмм. Для связи между ними используется два метода — передача параметров через стек и передача параметров через общую память. Рассмотрим передачу через стек.

Часто способ передачи параметров через стек оказывается удобнее, та как через регистры можно передать не более 6-7 параметров, а через стек сколько угодно. Еще одно преимущество — есть возможность написать процедуру с переменным количеством параметров.
Но есть и другая сторона медали: обращение к параметрам, расположенным в стеке, происходит медленнее. Если вы оптимизируете программу по скорости выполнения, то имеет смысл передавать параметры через регистры.

Помещение параметров в стек

Перед вызовом подпроцедуры передаваемые параметры необходимо поместить в стек используя команды PUSH. Существует два способа: параметры могут помещаться в стек в прямом или в обратном порядке. Как правило, используется обратный порядок, его мы и рассмотрим. Параметры помещаются в стек, начиная с последнего, так что перед вызовом процедуры на вершине стека оказывается первый параметр:

; Данные
arg0     dw 0
arg1     dw 12
argN     dw 345
;---------------------------------------------------------------------
; Код
    push [argN]
    push ...
    push [arg1]
    push [arg0]
    call myproc

Перед выполнением команды CALL стек будет иметь следующий вид:
img00

Обращение к параметрам внутри процедур

Для обращения к параметрам внутри подпроцедуры как правило используют регистр BP. В самом начале процедуры содержимое регистра BP сохраняется в стеке и в него копируется значение регистра SP. Это позволяет «запомнить» положение вершины стека и адресовать параметры относительно регистра BP.

;Процедура
myproc:
    push bp
    mov bp,sp
    ...</pre>

При выполнении кода процедуры стек будет иметь следующую структуру:
img01

Здесь ret_addr обозначает адрес возврата, помещаемый в стек командой вызова процедуры, а bp — сохранённое значение регистра BP. В нашем случае стек имеет ширину 16 бит, поэтому первый параметр будет доступен как word[bp+4], второй как word[bp+6] и так далее.

mov ax,[bp+4]       ;AX = arg0
mov bx,[bp+6]       ;BX = arg1
add ax,[bp+8]       ;AX = AX + arg2

Стоит представлять себе стек, чтобы правильно указывать смещения относительно регистра BP. Не забудьте перед возвратом из процедуры восстановить значение BP из стека.

Извлечение параметров из стека

После того, как процедура выполнилась, необходимо очистить стек, вытолкнув из него параметры. Тут тоже существует 2 способа: стек может быть очищен самой процедурой или кодом, который эту процедуру вызывал. Для первого способа используется команда RET с одним операндом, который должен быть равен количеству байтов, выталкиваемых из стека. В нашем случае он должен быть равен количеству параметров, умноженному на 2.

Для второго способа нужно использовать команду без операндов. Стек восстанавливается после выполнения процедуры путём прибавления значения к SP. С помощью такого способа программируются процедуры с переменным количеством параметров. Процедура не знает, сколько ей будет передано параметров, поэтому очистка стека должна выполняться вызывающим кодом.

push arg1
push arg0
call myproc2
add 4
;Восстановление указателя стека</span>
...
;Процедура с двумя параметрами (не очищает стек)</span>
myproc2:
push bp
mov bp,sp
...
pop bp
ret

Пример:
Главная программа:

.MODEL SMALL
TITLE MAIN
EXTRN SUB_PROC:FAR
.STACK 100h
.DATA
A DB 29,2,3,17
 DB 4,5,6,2
 DB 7,8,9,3
N EQU 3
M EQU 4

.CODE
.STARTUP
 
 MOV DX, OFFSET A
 PUSH DX
 PUSH M
 PUSH N
 CALL SUB_PROC 
 MOV AX, 4C00H ;ФУНКЦИЯ 4С - ВЫХОД В ОС
 INT 21H ;ПРЕРЫВАНИЕ ПРОГРАММЫ
 
END

Вызываемая подпрограмма:

.MODEL SMALL
TITLE SUB_PROC
PUBLIC SUB_PROC
.STACK 100h
.DATA
 max DB 0
 min DB 0 
 strmax DW 0
 strmin DW 0
 a DB 0
.CODE
SUB_PROC PROC FAR
PUBLIC SUB_PROC
 PUSH BP
 MOV BP, SP
 N EQU [BP+6]
 M EQU [BP+8]
 MOV bl, [BP+10]
 MOV a, bl
 
 xor bx, bx 
 mov cx, n 
M2: push cx
 mov cx, m
 xor si, si
M1: mov al, a[bx+si]
 cmp max, al
 ja m3
 mov max, al
 mov strmax, bx
 cmp al, min
 ja m3
 mov min, al
 mov strmin, bx
m3: inc si
 loop M1
 add bx,m
 pop cx
 loop M2
;обмен строк местами
 mov si, strmax
 mov di, strmin
 mov cx, m
M5:
 mov dl, [si] ; []-так передаем содержание по адресу (разименование), si(двубайтный) - изнасально хранит адрес
 xchg dl, [di] 
 mov [si], dl
 inc si
 inc di
 loop M5
 mov dx, offset a
 pop dx
 RET
SUB_PROC ENDP
END
Comments ( 0 )
    -->