Иллюстрированный самоучитель по Assembler

Использование подпрограмм

Преобразуем пример 3.8а так, чтобы единственный в этом примере параметр (условная величина задержки) передавался в подпрограмму не через регистр SI, а через стек.

Вызов подпрограммы delay в этом случае должен выполняться следующим образом:

push 2000; Проталкиваем в стек значение параметра
call delay; Вызываем подпрограмму delay

Текст подпрограммы подвергнется значительным изменениям:

Пример 3.8 в. Передача параметра через стек.

delay proc; Процедура-подпрограмма
push CX; Сохраним СХ основной программы
push BP; Сохраним BP
mov BP,SP; Настроим BP на текущую вершину стека
mov CX, [BP+6]; Скопируем из стека параметр
del1: push CX; Сохраним его
mov CX,0; Счетчик внутреннего цикла
del2 loop del2; Внутренний цикл(64К шагов)
pop CX; Восстановим внешний счетчик
loop del1; Внешний цикл
pop BP; Восстановим BP
pop CX; и СХ программы
ret 2; Возврат и снятие со стека ненужного уже параметра

Команда call, передавая управление подпрограмме, сохраняет в стеке адрес возврата в основную программу. Подпрограмма сохраняет в стеке еще два 16-разрядных регистра. В результате стек оказывается в состоянии, изображенном на рис. 3.9.

После сохранения в стеке исходного содержимого регистра ВР (в основной программе нашего примера этот регистр не используется, однако в общем случае это может быть и не так), в регистр ВР копируется содержимое указателя стека, после чего в ВР оказывается смещение вершины стека. Далее командой mov в регистр СХ заносится содержимое ячейки стека, на 6 байтов ниже текущей вершины. В этом месте стека как раз находится передаваемый в подпрограмму параметр, как это показано в левом столбце рис. 3.8. Конкретную величину смещения относительно вершины стека надо для каждой подпрограммы определять индивидуально, исходя из того, сколько слов сохранено ею в стеке к этому моменту. Напомним, что при использовании косвенной адресации с регистром ВР в качестве базового, по умолчанию адресуется стек, что в данном случае и требуется.

Иллюстрированный самоучитель по Assembler › Команды и алгоритмы › Использование подпрограмм
Рис. 3.8. Состояние стека в подпрограмме после сохранения регистров.

Параметр, полученный таким образом, используется далее в подпрограмме точно так же, как и в примере 3.8 а.

Выполнив возложенную на нее задачу, подпрограмма восстанавливает сохраненные ранее регистры и осуществляет возврат в основную программу с помощью команды ret, в качестве аргумента которой указывается число байтов, занимаемых в стеке отправленными туда перед вызовом подпрограммы параметрами. В нашем случае единственный параметр занимает 2 байт. Если здесь использовать обычную команду ret без аргумента, то после возврата в основную программу параметр останется в стеке, и его надо будет оттуда извлекать (между прочим, не очень понятно, куда именно, ведь все регистры у нас могут быть заняты). Команда же с аргументом, осуществив возврат в вызывающую программу, увеличивает содержимое указателя стека на значение ее аргумента, тем самым осуществляя логическое снятие параметра. Физически этот параметр, как, впрочем, и все остальные данные, помещенные в стек, остается в стеке и будет затерт при дальнейших обращениях к стеку.

Разумеется, в стек можно было поместить не один, а сколько угодно параметров. Тогда для их чтения надо было использовать несколько команд mov со значениями смещения ВР+6, ВР+8, BP+0Ah и т.д.

Рассмотренная методика может быть использована и при дальних вызовах подпрограмм, но в этом случае необходимо учитывать, что дальняя команда call сохраняет в стеке не одно, а два слова, что повлияет на величину рассчитываемого смещения относительно вершины стека.

Если Вы заметили ошибку, выделите, пожалуйста, необходимый текст и нажмите CTRL + Enter, чтобы сообщить об этом редактору.