Файл: 12. Передача параметров через стек. Локальные данные подпрограмм.pdf

ВУЗ: Не указан

Категория: Учебное пособие

Дисциплина: Программирование

Добавлен: 30.10.2018

Просмотров: 404

Скачиваний: 9

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
background image

12. Передача параметров через стек. Локальные данные подпрограмм 

Этот способ важен для следующих случаев: 

 

рекурсивные вызовы в ассемблерных программах 

 

передача параметров при вызове ассемблерных программ из программ на 

языках высокого уровня 

 

вызов API-функций в ассемблерных Windows-программах 

Необходимые основные шаги: 

 

основная  программа  перед  вызовом  подпрограммы  помещает  в  стек 

входные значения 

 

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

 

после завершения работы подпрограммы производится очистка стека для 

восстановления его состояния до вызова подпрограммы. 

Взаимодействие  подпрограмм  может  выполняться  двумя  способами  –  в  так 

называемом Pascal-стиле и С-стиле. 

Особенности Pascal-стиля: 

 

входные параметры записываются в стек в их естественном порядке, т.е. 

так, как они объявлены в заголовке - слева направо, после чего в вершине 

стека оказывается самый последний параметр 

 

за очистку стека отвечает подпрограмма 

Особенности С-стиля: 

 

параметры в стек записывается в обратном порядке: сначала последний, 

потом  предпоследний  и  т.д.,  в  итоге  на  вершине  оказывается  первый 

параметр 

 

очистку  стека  выполняет  основная  программа  после  возврата  из 

подпрограммы 

В качестве примера далее рассматривается способ взаимодействия в Pascal-

стиле.  Пусть  в  подпрограмму  надо  передать  3  параметра  р1,  р2  и  р3.  Тогда 

типичный фрагмент кода основной программы будет: 

PUSH   p1 

PUSH   p2 


background image

PUSH   p3 

CALL   ИмяПП 

Отметим, что команда CALL запишет в вершину стека адрес возврата, и 

тем самым состояние стека при входе в подпрограмму будет следующим: 

 

 

 

 

адрес 

возврата 

р3 

р2 

р1 

 

 

стековая память 

 

 

После вызова подпрограмме нужны параметры, но использовать команду 

РОР    нельзя,  т.к.  при  выталкивании  пропадает  адрес  возврата.  Приходится 

использовать  регистр  EBP,  установив  в  него  значение  из  ESP  и  используя 

выражение  вида  [EBP  + i].  Но  регистр  EBP  может использоваться  в основной 

программе,  поэтому  предварительно  его  надо  сохранить  в  стеке,  а  потом  уж 

использовать. Для всех этих операций в начале подпрограммы оформляется так 

называемый “пролог”: 

MyProc 

PROC 

 

 

PUSH    EBP 

          ; (EBP) 

 стек 

 

 

MOV     EBP,  ESP 

; (EBP) = вершина стека 

После выполнения этих действий состояние стека будет следующим: 

 

 

 

 

старый 

рег. EBP 

адрес 

возврата 

р3 

р2 

р1 

 

 

стековая память 

 

 

 

Тогда  [EBP  +  8]  –  адрес  последнего  параметра  (третьего),  [EBP  +  12]  – 

предпоследнее  (второго),  а  [EBP  +  16]  –  первого.  Эти  выражения  можно 

регистр ESP 

+ 4 

+ 8 

+ 12 

регистр ESP 

+ 4 

+ 8 

+ 12 

+ 16 

регистр EBP 


background image

использовать  в  теле  подпрограммы  для  выполнения  необходимых  действий  с 

параметрами. Регистр EBP при этом изменяться не должен. 

В  конце  подпрограммы  надо  выполнить  “выходные”    действия,  т.е. 

оформить  так  называемый  “эпилог”.  Прежде  всего  надо  учитывать,  что  к 

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

и  в  начале  работы  подпрограммы.  Поэтому  если  подпрограмма  использовала 

стек  для  своих  внутренних  нужд,  надо  вернуть  его  состояние  в  начальное, 

например – командой MOV  ESP, EBP.  

После  этого  надо  считать  с  вершины  старое  значение  регистра  EBP, 

потом  –  адрес  возврата  и  очистить  стек  от  входных  параметров.  Для 

подпрограмм,  взаимодействующих  в  Pascal-стиле  можно  использовать 

специальную  разновидность  команды  выхода  из  подпрограммы  вида  RET  N, 

где N – число байтов, автоматически удаляемых из стека после извлечения из 

стека адреса возврата.  

Надо учитывать, что N – это число байтов, а так как параметры в стеке – 

это двойные слова, то для удаления К параметров надо задать  N = 4*K. Тогда 

эпилог в Pascal-стиле будет следующим: 

POP   EBP      ;  восстановим старое значение регистра EBP 

RET   N           ;  удалить из стека K параметров и сделать возврат. 

На эту простейшую схему часто накладывается необходимость восстановления 

сохраненных в стеке РОНов. 

Пример.  Оформить  подпрограмму  обнуления  N  байтов  в  массиве  с 

начальным  адресом  Mas.  Входные  параметры  -  адрес  массива  и  количество 

обнуляемых  байтов  –  передаются  через  стек  с  помощью  Pascal-модели. 

Фрагмент основной программы: 

LEA     EAX,   Mas    ;  адрес массива – в регистр EAX 

PUSH   EAX               ;  а потом из регистра – в стек 

PUSH   100                 ;  в стек – число обнуляемых байтов 

CALL   NullMas         ;  вызов подпрограммы с именем NullMas 

При входе в подпрограмму состояние стека будет следующим: 

регистр ESP 

+ 4 

+ 8 


background image

 

 

 

 

 

адрес 

возврата 

число 

байтов 

адрес 

массива 

 

 

стековая память 

 

 

Для  циклического  обнуления  байтов  в  массиве  подпрограмма  должна 

использовать  два  регистра  –  ECX  как  счетчик  цикла  и  EBX  в  качестве 

хранилища  адреса  очередного  байта  массива  (косвенная  адресация).  Поэтому 

при входе в подпрограмму значения этих регистров должны быть сохранены в 

стеке,  а  при  завершении  подпрограммы  –  восстановлены.  Пролог  в 

подпрограмме обнуления будет иметь следующий вид: 

PUSH    EBP             ;  сохранить регистр EBP 

MOV     EBP, ESP    ; загрузить в EBP адрес вершины стека 

PUSH    EBХ            ;  сохранить регистр  EBХ 

          PUSH    EСХ            ;  сохранить регистр  EСХ 

После этих операций состояние стека будет следующим: 

 

 

 

 

старый 

ECX 

старый 

EBX 

старый 

EBP 

адрес 

возврата 

число 

байтов 

адрес 

массива 

 

 

стековая память 

 

 

Из  этой  схемы  легко  можно  увидеть,  как  с  помощью  регистра  EBP 

подпрограмма  может  получить  доступ  к  обоим  входным  параметрам.  Сама 

подпрограмма будет следующей:  

NullMas    PROC   

        “пролог” 

        MOV     ECX, [EBP + 8]          ;  (ECX) = число обнуляемых байтов 

        MOV     EBX, [EBP + 12]        ;  (EBX) = адрес массива 

регистр ESP 

регистр EBP 

+ 4 

+ 8 

+ 12 


background image

L:              MOV     BYTE   PTR  [EBX], 0  ; обнуление очередного байта 

       INC       EBX                               ;  переход к следующему байту 

       LOOP    L 

 

       POP      ECX    ;   восстановление EСХ 

       POP      EBX    ;   восстановление EBХ 

       POP      EBP     ;   восстановление EBP 

       RET     8         ;     возврат и удаление восьми байтов 

NullMas   ENDP 

 

Довольно  часто  подпрограммам  для  выполнения  своих  функций 

требуется использовать временные переменные только на время своей работы. 

Как  известно,  такие  переменные  принято  называть  локальными.  Если  их 

немного, можно использовать регистры, предварительно сохранив их значения 

в стеке с последующим восстановлением. Если регистров недостаточно, можно 

использовать  стек,  выделяя  в  нем  при  запуске  подпрограммы  необходимую 

область и освобождая ее при завершении. В этом случае место под  локальные 

переменные выделяется только на время работы подпрограммы. 

При входе в подпрограмму со стеком надо сделать следующие операции: 

 

запомнить старое значение регистра EBP 

 

установить регистр EBP на вершину стека 

 

уменьшить значение в регистре ESP на количество байт, необходимое для 

хранения значений локальных данных  

Например,  если  подпрограмме  надо  использовать  две  локальные  переменные, 

то в начале подпрограммы необходимо: 

PUSH   EBP 

MOV    EBP, ESP 

SUB     ESP, 8 

; уменьшить ESP на два двойных слова 

Состояние стека после этого будет следующим: 

 

 

регистр ESP 

регистр EBP