На этом шаге мы рассмотрим передачу параметров через стек.
Этот способ наиболее часто используется для передачи аргументов при вызове процедур. Суть его заключается в том, что вызывающая процедура самостоятельно заносит в стек передаваемые данные, после чего производит вызов вызываемой процедуры. При передаче управления процедуре микропроцессор автоматически записывает в вершину стека два (для процедур типа 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].
Конец процедуры также должен быть оформлен особым образом и содержать действия, обеспечивающие корректный возврат из процедуры. Фрагмент кода, выполняющего такие действия, имеет свое название - эпилог процедуры. Код эпилога должен восстановить контекст программы в точке вызова вызываемой процедуры из вызывающей программы. При этом, в частности, нужно откорректировать содержимое стека, убрав из него ставшие ненужными аргументы, передававшиеся в процедуру. Это можно сделать несколькими способами:
На следующем шаге мы рассмотрим передачу аргументов через общую область памяти.