Шаг 36.
Передача аргументов через стек

    На этом шаге мы рассмотрим передачу параметров через стек.

    Этот способ наиболее часто используется для передачи аргументов при вызове процедур. Суть его заключается в том, что вызывающая процедура самостоятельно заносит в стек передаваемые данные, после чего производит вызов вызываемой процедуры. При передаче управления процедуре микропроцессор автоматически записывает в вершину стека два (для процедур типа NEAR) или четыре (для процедур типа FAR) байта. Эти байты являются адресом возврата в вызывающую программу. Если перед передачей управления процедуре командой CALL в стек были записаны переданные процедуре данные или указатели на них, то они окажутся под адресом возврата.

    При рассмотрении архитектуры микропроцессора мы выяснили, что стек обсуживается тремя регистрами: SS, SP и BP. Микропроцессор автоматически работает с регистрами SS и SP в предположении, что они всегда указывают на вершину стека. По этой причине их содержимое изменять не рекомендуется. Для осуществления произвольного доступа к данным в стеке архитектура микропроцессора имеет специальный регистр BP (Base Point - указатель базы). Перед использованием этого регистра для доступа к данным стека его содержимое необходимо правильно инициализировать, что предполагает формирование в нем адреса, который бы указывал непосредственно на переданные данные. Для этого в начало процедуры рекомендуется включить дополнительный фрагмент кода. Он имеет свое название - пролог процедуры. Типичный фрагмент программы, содержащий вызов процедуры с передачей аргументов через стек, может выглядеть так:

             TITLE  Передача параметров через стек.
             ;Программа оформлена как COM-файл.
CodeSg       SEGMENT  PARA
             ASSUME CS: CodeSg
             ORG 100h
Begin        PROC    
             MOV AX,2
             PUSH AX        ;Запись в стек первого аргумента.
             MOV BX,3
             PUSH BX        ;Запись в стек второго аргумента.
             CALL Summa     ;Обращение к процедуре.
             RET
Begin        ENDP
Summa        PROC     NEAR  ;"Близкая" процедура с n (n=2) аргументами.
             ;Начало пролога.
             PUSH BP
             MOV BP,SP
             ;Конец пролога.
             MOV DX,[BP+4] ;Загрузка  второго аргумента. 
             ADD DX,[BP+6] ;Сложение второго аргумента с первым. 
             ADD DL,30H    ;Получили код выводимой цифры.
             MOV AH,2
             INT 21H       ;Выводим символ на экран.
             ;Начало эпилога.
             MOV SP,BP     ;Восстановление SP.
             POP BP        ;Восстановление BP.
             RET  4        ;Возврат в вызывающую программу.
             ;Конец эпилога.
Summa        ENDP
CodeSg       ENDS
             END  Begin
Текст этой программы можно взять здесь.

    Код пролога состоит всего из двух команд. Первая команда PUSH BP сохраняет содержимое BP в стеке с тем, чтобы исключить изменение находящегося в нем значения в вызываемой процедуре. Вторая команда пролога MOV BP,SP настраивает BP на вершину стека. После этого мы можем не волноваться о том, что содержимое SP перестанет быть актуальным, и осуществлять прямой доступ к содержимому стека. Что мы и делаем. Для доступа к аргументу с номером n достаточно сместиться от содержимого BP на 4 байта, для доступа к аргументу n-1 - на 6 и т. д. Но эти смещения подходят только для процедур типа NEAR. Для FAR-процедур эти значения необходимо увеличить на 2 байта, так как при вызове процедуры дальнего типа в стек записывается полный адрес - содержимое CS и IP. Поэтому для доступа к аргументу с номером n команда будет выглядеть так: MOV AX,[BP+6].

    Конец процедуры также должен быть оформлен особым образом и содержать действия, обеспечивающие корректный возврат из процедуры. Фрагмент кода, выполняющего такие действия, имеет свое название - эпилог процедуры. Код эпилога должен восстановить контекст программы в точке вызова вызываемой процедуры из вызывающей программы. При этом, в частности, нужно откорректировать содержимое стека, убрав из него ставшие ненужными аргументы, передававшиеся в процедуру. Это можно сделать несколькими способами:

    На следующем шаге мы рассмотрим передачу аргументов через общую область памяти.




Предыдущий шаг Содержание Следующий шаг