мещены в стек, теперь должны быть удалены. Вызывающая программа
может удалить параметры либо извлечением из стека (путем исполь-
зования инструкции POP), либо просто добавлением хранимых пара-
метров в регистр SP, например, по инструкции add SP,N, где N
представляет собой количество байтов, занимаемых параметрами.
Этот способ, показанный в листинге 2-1, эффективно урезает стек в
первоначальное положение. Альтернативно ответственность за очист-
ку стека может быть назначена вызываемой программе путем исполь-
зования инструкции RET N, где N опять количество байтов, занимае-
мое параметрами. При любом способе N равно количеству помещенных
с помощью инструкции PUSH слов, умноженное на 2.
Различие между этими двумя способами состоит в том, что при
использовании инструкции RET N программа должна вызываться в
- 2-11 -
точности с правильным количеством параметров. Если имеется не N
байтов параметров, то инструкция RET N неправильно выровняет стек
и произойдет авария системы. Напротив, если стек очищает вызываю-
щая программа путем использования инструкции add SP,N, то каждый
вызов в целевую программу может передавать различное количество
параметров.
Листинг 2-1. Передача параметров в стек
-----------------------------------------------------------------
; Вызывающая процедура
... ...
push ; пересылка последнего аргумента
... ...
push ; пересылка второго аргумента
push ; пересылка первого аргумента
call ; вызов процедуры
add sp,<2N> ; очистка стека
... ...
; Вызываемая процедура
PROC NEAR ; пример вызова процедуры near
push bp ; сохранение старого BP
mov bp,sp ; указатель ссылки на стек
... ...
mov ,[bp+4] ; доступ к первому параметру
mov ,[bp+6] ; доступ ко второму параметру
... ...
mov ,[bp+2+2N] ; доступ к последнему параметру
... ...
mov sp,bp ; восстановление SP
pop bp ; удаление сохраненного BP
ret ; возврат в вызывающую программу
ENDP
----------------------------------------------------------------
До тех пор, пока вызывающая программа обрабатывает стек пра-
вильно, проблем не будет. (Конечно, если вызываемая программа
сможет использовать различное количество параметров, выдаваемых
от вызова к вызову).
С целью облегчения программирования следует заменить простой
вызов внешними участками программы, использующими инструкции PUSH
(записать в стек), MOV (переслать), POP (извлечь из стека) и про-
чие. Это как раз одно из мест для вывода известных и простых мак-
росов для выполнения этих рутинных операций. Макросы, приведенные
в листинге 2-2, помогают вызывающей программе поддерживать стек
во время передачи параметров. Аналогично, макросы, приведенные в
листинге 2-3, помогают вызываемой программе при доступе и возвра-
те параметров из стека. Все регистры, используемые в этих макро-
сах, должны быть длиной в слово, потому что инструкции PUSH и POP
не работают с 8-битовыми регистрами.
- 2-12 -
Листинг 2-2. Макросы @CallS и @FCallS для передачи параметров стек
------------------------------------------------------------------
;; **** Макрос @PushIm: запись в стек непосредственных данных
;; через регистр BP
@PushIm MACRO arg
mov cs:mem_16,&arg
push cs:mem_16
ENDM
;; **** Макрос Вызов подпрограммы: @Calls имя,
@CallS MACRO routine_name,arg_list
?count = 0
IRP argn,<&arg_list>
push &&argn ; передача параметра
?count = ?count+1
ENDM
@PushIm %?count ; передача количества параметров
call &routine_name ; вызов программы
add sp,2*(1+?count) ; очистка стека
ENDM
;; **** Макрос Вызов функции: @FCallS имя,
@FCallS MACRO routine_name,arg_list,return_val
?count = 0
IRP argn,<&arg_list>
push &&argn ; передача параметра
?count = ?count+1
ENDM
@PushIm %?count ; передача количества параметров
call &routine_name ; вызов программы
pop &return_val ; получение возвращаемого знач-я
if ?count ; если не нуль ...
add sp,2*?count ; очистка стека
ENDIF ENDM
-----------------------------------------------------------------
Листинг 2-3. Макросы @Accept,@RetVal и @CRet
для приема в стек и возврата параметров из стека
-----------------------------------------------------------------
;; **** Макрос @RetVal: @RetVal регистр
@RetVal MACRO return_value ; возвращаемое значение
mov [bp+4],return_val ; возврат слова
ENDM
;; **** Макрос @Accept: @Accept
@Accept MACRO reg_list
push bp ; сохранение указателя базы
mov bp,sp ; уст-ка BP для доступа к парам.
mov &pnum,[bp+4] ; получение количества парам-ов
?count = 0
IRP reg,<®_list>
?count = ?count+1
push &® ; сохр-е рег-ра для нового знач.
mov &®,[bp+4+?count*2] ; получение параметра
ENDM
ENDM
;; **** Макрос @CRet: @CRet
@CRet MACRO reg_list ; список регистров
IRP reg,<®_list>
pop &® ; восст-е сохраненного регистра
ENDM
pop bp ; восстановление указателя базы
ret ; возврат из программы
ENDM
- 2-13 -
Макрос @PushIm позволяет пользователям микропроцессоров
8086/8088 помещать непосредственные данные в стек. Для использо-
вания макроса сначала необходимо определить в программном сегмен-
те местоположение слова mem_16. Несмотря на то, что передача не-
посредственных данных в стек медленная и принимаются большие
коды, такой алгоритм работы создает большую свободу использования
регистров.
Символ ?count в макросах @CallS и @FCallS используется для
сообщения вызываемой программе количества предусмотренных пара-
метров; для приема количества байтов, помещенных в стек; и для
использования при очистке стека после вызова. Если целевая или
вызываемая программа уже знает сколько параметров было в нее пе-
редано (обычно является случайным), то эти макросы должны быть
модифицированы, чтобы обойтись без передачи и очистки счетчика
параметров. Заметим, что счетчик параметров также используется в
качестве поля возврата значения для вызова функций (макросы
@FCallS и @RetVal).
Макрос @RetVal предназначен для использования с макросом
@FCallS и замещает счетчик параметров, помещенный в стек с по-
мощью макроса @FCallS, 16-битовым значением для возврата в вызы-
вающую программу.
Макрос @Accept целевой программы работает либо с макросом
@CallS, либо с макросом @FCallS для передачи параметров из стека в
регистр. Этот макрос сохраняет регистры, используемые в процессе
работы. Символ ?count используется здесь для определения смещения
следующего параметра в стеке. В связи с тем, что макрос @Accept
работает в направлении вверх по стеку (увеличение смещения), то
этот макрос выбирает параметры из стека в порядке, обратном тому,
в котором они были помещены в стек! Заметим также, что оба макроса
@Accept и @RetVal предполагают вызов процедуры near (близкий),
поскольку они допускают только 2-байтовый адрес возврата.
Последний целевой макрос @CRet восстанавливает регистры, ко-
торые были сохранены макросом @Accept. В связи с тем, что инс-
трукции POP должны быть в обратном порядке по отношению к инс-
трукциям PUSH, список аргументов для макроса @CRet должен
располагаться в порядке, обратном тому, какой был при выполнении
макроса @Accept. Последним действием, предпринимаемым перед инс-
трукцией RET, является восстановление указателя базы, сохраненно-
го макросом @Accept.
Приведенные макросы представлены здесь скорее в качестве при-
меров, нежели рабочих копий и могут быть улучшены для обеспечения
более полного использования. Например, параметр инструкции PUSH
(push &&argn) для обработки непосредственных данных в качестве
параметров может быть замещен более общим макросом PushOp из гла-
вы 1. Одним из ограничений текущей версии является то, что инс-
трукция mov [bp+4],return_value в макросе @RetVal не может возв-
ращать переменные памяти в стек, потому что семейство микропро-
цессоров 8086 не поддерживает инструкцию пересылки память-па-
мять. Для распознавания пересылки память-память и генерации пере-
дачи через непосредственный регистр этот макрос должен быть
переделан.
Кроме того, необходимо иметь в виду, что макросы, представ-
ленные в листингах 2-2 и 2-3, реализуют вызывающую программу, ко-
торая несовместима ни с одним известным языком высокого уровня.
Характерно, что эти процедуры в качестве дополнительного аргумен-
- 2-14 -
та передают количество аргументов для вызываемой процедуры и
возвращают значения для вызывающей процедуры непосредственно в
стек.
MASM обеспечивает для вызываемой программы некоторые средс-
тва, упрощающие доступ к данным в стеке. Благодаря описанию
structure (структура), которая описывает данные в стеке и вырав-
нивает указатель базы (BP) на начало структуры, к данным в стеке
можно обращаться по символическим именам. Это помогает предотвра-
щать фатальные ошибки кодирования, которые являются результатом
указания неправильного смещения. Листинг 2-4 демонстрирует дирек-
тиву MASM STRUC в этом контексте.
Листинг 2-4. Символический доступ к содержимому стека