Стек также служит удобным местом для передачи информации в
подпрограммы и из них. Обычно программа передает параметры в
подпрограмму, помещая их в регистры, однако в некоторых случаях
число параметров превышает размеры регистрового пространства. В
таких случаях програииа может поместить параметры в стек до
выполнения команды CALL (вызов подпрограммы). Как мы увидим в
гл.10, стек является единственным средством передачи параметров в
подпрограммы, написанные на языке ассемблера, из языков высокого
уровня Бейсик и Фортран.
Подпрограмма может очень эффективно загружать эти параметры из
стека. В обычных случаях программа читает информацию из стека
единственным способом - извлекая ее оттуда. Вместо этого
подпрограмма может использовать регистр BP, как указатель на
область стека. Когда программа передает параметры через стек, одной
из первых команд в подпрограмме выполняется команда
MOV BP, SP
которая загружвет регистр BP текущим значением указателя стека.
Поскольку регистр BP - адресный регистр, подпрограмма может
использовать его при адресных вычислениях, а это означает, что все
параметры доступны как смещения относительно регистра BP.
Конструкторы микропроцессора 8088 определенно помнили об
описанном выше методе передачи параметров, так как при доступе к
данным регистр BP использует по умолчанию регистр стекового
сегмента SS в качестве сегментного регистра. Во всех других
нормальных случаях доступа к данным микропроцессор использует
регистр DS. Поскольку стек находится в стековом сегменте,
регистровую пару SS:BP очень естественно использовать для адресации
информации в стеке.
На Фиг. 4.7 изображен пример, демонстрирующий использование
регистра BP для доступа к параметрам, переданным через стек. В этом
примере головная программа перед выполнением команды CALL поместила
четыре слова в стек. Подпрограмма загружает в BP указатель данных в
стеке. Заметим, что смещения, используемые для доступа к данным в
стеке, учитывают тот факт, что адрес возврата также был записан в
стек в результате выполнения команды CALL.
В подпрограмме этого примера в вершине стека лежит адрес
возврата, и регистр BP содержит смещение этой ячейки. Двумя байтами
ниже в стеке лежит помещенный последним параметр, регистр DX;
далее, через двухбайтовые интервалы - регистры CX, BX и AX. Таким
образом, правильным адресом для чтения параметра, содержащегося в
регистре DX, будет [BP+2], а другие адреса следуют через
двухбайтовые интервалы. В данном примере значение, находившееся в
регистре DX, попадает в регистр AX, CX в BX и т.д.
Подпрограмма может использовать регистр BP для адресации стека
не только при передаче параметров. Подпрограмма может оказаться
длинной и запутанной настолько, что хранить все необходимые ей во
время выполнения значения в регистрах трудно. Помещение этих
значений в стек и загрузка указателя этой области в регистр BP
решает проблему.
Многим подпрограммам в течение их выполнения также необходима
локальная память, и подпрограммы могут динамически расположить ее в
стеке. Всякий раз, когда программа вызывается, она может вычесть
размер этой области памяти из содержимого указателя стека. Так как
стек растет по направлению к младшим адресам, вычитание числа из
регистра SP идентично помещению в стек такого же количества данных
- за исключением тех данных, которые не инициализированы. После
этого подпрограмма может использовать регистр BP для адресации
такой области памяти. Когда наступает момент возврата, подпрограмма
может прибавить соответствующее значение к указателю стека, и тем
самым восстановить его прежнее значение. Динамическая организация
данных означает, что программа использует область памяти только
тогда, когда она необходима для работы, и не занимает эту память
все остальное время, поэтому программу можно выполнять на машине с
малым объемом памяти, что невозможно при другой организации данных.
Но лучшим является то, что программист не должен создавать сложную
подсистему управления памятью, так как все находится под
управлением стековой структуры.
Оператор возврата из подпрограммы на Фиг. 4.7 демонстрирует еще
одну возможность набора команд микропроцессора 8088. Команда
возврата из подпрограммы RET может иметь операнд, который
представляет собой значение, прибавляемое микропроцессором к
содержимому указателя стека после извлечения адреса возврата. В
примере используется значение 8; это означает, что восемь байт, или
четыре слова данных должны быть удалены из стека после возврата.
Эти значения исчезают навсегда. Результат тот же, какой был бы в
итоге извлечения значений из стека, чтобы уничтожить их; команда
возврата уже сделала это автоматически.
Такой метод удаления информации из стека срабатывает только в
случае параметров, которые вызывающая программа помешает в стек.
Подпрограмма обязана удалить все динамически распределенные области
памяти из стека перед выполнением возврата. Она должна сделать это
явно, а не с помощью команды возврата, так как область данных лежит
между текущей вершиной стека и адресом возврата.
Подпрограмма может возвратить в стеке некоторую информацию
вызывающей программе. Если вызывающая программа помешает параметры
в стек, подпрограмма может изменить их значения и оставить в стеке,
а вызывающая программа может извлечь их после возврата. Если
подпрограмма возвращает только один параметр, но вызывалась с тремя
параметрами в стеке, то выполнить возврат она может с помощью
команды RET 4. При этом последние два параметра извлекаются из
стека и только возвращаемый параметр остается в стеке.
В гл.10, где мы используем
подпрограммы на языке ассемблера с
языками высокого уровня, головная
программа помещает параметры в
стек. Но эти параметры - адреса данных, а
не собственно данные. Это
означает, что ассемблерная подпрограмма не
должна возвращать
параметры в стеке и обязана извлечь все
параметры из стека при
возврате.