При написании программ на языке ассемблера часто приходится разбивать свой код на несколько подпрограмм. Для связи между ними используется два метода — передача параметров через стек и передача параметров через общую память. Рассмотрим передачу через стек.
Часто способ передачи параметров через стек оказывается удобнее, та как через регистры можно передать не более 6-7 параметров, а через стек сколько угодно. Еще одно преимущество — есть возможность написать процедуру с переменным количеством параметров.
Но есть и другая сторона медали: обращение к параметрам, расположенным в стеке, происходит медленнее. Если вы оптимизируете программу по скорости выполнения, то имеет смысл передавать параметры через регистры.
Помещение параметров в стек
Перед вызовом подпроцедуры передаваемые параметры необходимо поместить в стек используя команды PUSH. Существует два способа: параметры могут помещаться в стек в прямом или в обратном порядке. Как правило, используется обратный порядок, его мы и рассмотрим. Параметры помещаются в стек, начиная с последнего, так что перед вызовом процедуры на вершине стека оказывается первый параметр:
; Данные arg0 dw 0 arg1 dw 12 argN dw 345 ;--------------------------------------------------------------------- ; Код push [argN] push ... push [arg1] push [arg0] call myproc
Перед выполнением команды CALL стек будет иметь следующий вид:
Для обращения к параметрам внутри подпроцедуры как правило используют регистр BP. В самом начале процедуры содержимое регистра BP сохраняется в стеке и в него копируется значение регистра SP. Это позволяет «запомнить» положение вершины стека и адресовать параметры относительно регистра BP.
;Процедура myproc: push bp mov bp,sp ...</pre>
При выполнении кода процедуры стек будет иметь следующую структуру:
Здесь 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 )