по директиве STRUC
-----------------------------------------------------------------
; Вызывающая процедура
... ...
push ; пересылка 1-го аргумента
push ; пересылка 2-го аргумента
... ...
push ; пересылка последнего аргумента
call ; вызов процедуры
... ...
; Вызываемая процедура
StackFrame STRUC ; описание шаблона стека
dw ? ; сохраненный BP
dd ? ; адрес возврата (используйте "dw"
; для NEAR (близкий))
paramN dw ? ; последний параметр
... ...
param2 dw ? ; 2-й параметр
param1 dw ? ; 1-й параметр
StackFrame ENDS ; конец описания шаблона
;
base EQU [bp] ; база шаблона
;
PROC FAR ; пример вызова far (далеко)
push bp ; сохранение старого BP
mov bp,sp ; указатель ссылки в стеке
... ...
mov ,base.param1 ; доступ к 1-му параметру
mov ,base.param2 ; доступ ко 2-му параметру
... ...
mov ,base.paramN ; доступ к последнему пар-ру
... ...
mov sp,bp ; восстановление SP
pop bp ; сброс сохраненного BP
ret (2N) ; возврат в вызывающую программу
ENDP
----------------------------------------------------------------
Листинги 2-1 и 2-4 различаются по трем важным аспектам. Пер-
вое отличие заключается в порядке помещения параметров в стек. В
- 2-15 -
листинге 2-1 вызывающая программа размещает свои параметры в сте-
ке в обратном порядке (от последнего к первому), в то время как в
листинге 2-4 в прямом порядке (от первого к последнему). Для
структуры StackFrame при работе с листингом 2-1 порядок парамет-
ров должен быть изменен на противоположный (т.е. реверсирован).
(Назначение порядка "от первого к последнему" может привести к
путанице в этой точке. На самом деле назначается порядок следова-
ния параметров "слева - направо", т.е. как они появлялись бы на
языке высокого уровня).
Второе отличие между примерами заключается в способе очистки
переданных параметров из стека. В примере, приведенном в листинге
2-1, вызывающая программа очищает параметры в стеке посредством
инструкции add SP,<2N>. В листинге 2-4 вызываемая программа очи-
щает стек, используя инструкцию ret (2N).
Последнее отличие заключается в том, что в листинге 2-1 пока-
зана программа near (близкий), в то время как в листинге 2-4 по-
казана программа far (далекий). Если структура StackFram будет
использоваться с процедурой near, то необходимо заменить директи-
ву dd директивой dw. Это вызывает резервирование в шаблоне для
адреса возврата вызывающей программы только двух байтов, в то
время как для вызываемой процедуры far требуется четыре байта. С
другой стороны, если структура будет использована в программе
прерывания, то после директивы dd необходимо будет добавить до-
полнительную директиву dw для резервирования памяти для флажков
процессора, помещаемых в стек при прерывании.
Директива STRUC не выполняет добавление никакого кода в ко-
нечную программу. Эта директива только описывает смещения, ис-
пользуемые с указателем базы BP, для облегчения задачи обращения
к параметрам.
Стек также обеспечивает удобное место для хранения возвращае-
мых значений, но мы отложим обсуждение этой темы до тех пор, пока
не обсудим различия между функциями и подпрограммами в последую-
щих разделах этой главы.
Краткое изложение опций передачи параметров
Существует три возможных способа передачи данных модулям:
1. Передача через регистры - допускается несколько парамет-
ров; это наилучший способ для простого интерфейса и для обработки
исключительных ситуаций, а также для возврата значений.
2. Передача через общую область памяти - ограничивает гиб-
кость и общность модулей, но имеет преимущества обеспечения дос-
тупности данных для всех модулей.
3. Передача данных в стек - предпочтительный способ обработки
данных; превосходит в общности (многократно-используемые модули)
и вырабатывает модульные программы; необходим для интерфейса с
большинством языков высокого уровня.
Кроме того, при передаче данных способом, отличным от выше-
приведенных, каждый модуль должен принимать в качестве параметров
необходимые ему данные не только для себя самого, но и для других
модулей, которые он вызывает в процессе своей работы. Иногда это
может привести к большому списку параметров для модулей верхнего
уровня.
В действительности, по всей вероятности, для организации пе-
редачи данных модулю может потребоваться использование комбинации
этих способов (за исключением передачи данных в память програм-
мы).
- 2-16 -
Передача параметров по значению или адресу
После принятия решения о том, как передавать параметры, необ-
ходимо ответить на вопрос: в какой форме использовать аргументы?
Вспомните, что аргумент представляет собой вызываемое значение,
которое присваивается параметру. Это значение может быть либо не-
посредственными данными, либо адресом данных.
Передача по значению
Чаще всего передача параметров в языке Ассемблер выполняется
путем передачи значения. При этом способе передачи в вызывающую
программу передаются действительные данные (их значения). Целевая
программа получает число, хранящееся в регистре, либо помещенное
в стек.
Хранение данных в общей памяти может представлять собой не-
сколько специальных случаев. В первом случае данные передаются по
адресу, поскольку вызывающая и вызываемая программы осуществляют
обращение к данным посредством значений общих адресов. В другом
случае данные в общей области могут быть либо значениями, либо
адресами, и проблема упрощается, базируясь на решении о природе
данных в общем блоке. Если данные являются значениями, то они пе-
редаются по значению. Если данные являются адресом, то они пере-
даются по адресу.
Если параметры, содержащие непосредственные данные, передают-
ся в стек, то пользователи микропроцессоров 8086 и 8088 не должны
испытывать страх перед некоторыми дополнительными усилиями, свя-
занными с передачей значений в стек. Пользователи усовершенство-
ванного микропроцессора 80x86 могут использовать инструкцию
PUSH
(переслать в стек непосредственные данные), однако пользователи
других микропроцессоров должны передавать данные в стек через не-
посредственный регистр. Для этого может быть использован макрос
@PushIm, рассмотренный в главе 1, однако, для этого приложения
его сложность не указывается. Если используется вызывающая прог-
рамма, приведенная в листинге 2-1, то для передачи непосредствен-
ных данных в стек доступен регистр BP (указатель базы). Почти во
всех соглашениях по архитектуре микропроцессоров 8086 для этих
целей предназначен регистр AX. Любые непосредственные данные, ко-
торые необходимо переслать в стек, передаются с помощью следующих
двух строк программы:
mov ax,
push ax
Способ передачи параметров по значению унаследовал ограниче-
ние передаваемого значения при использовании регистра и передаче
данных в стек до 16 бит. На самом деле, 8-битовые данные вообще
не могут быть помещены в стек. Конечно же, имеются пути обхода
этого ограничения, примером этого является макрос @PushOp из гла-
вы 1. Данные, относящиеся к большим структурам, иногда могут быть
переданы в стек словами, но если вызываемая программа не должна
получать свои параметры из стека, то передача адреса данных нам-
ного удобней.
- 2-17 -
Передача по адресу
При передаче по адресу вызываемая программа получает только
адрес данных. Доступ к данным осуществляется путем использования
этого адреса. Имеется несколько непосредственных преимуществ дан-
ного способа. Первое состоит в том, что если данные не расположены
в различных сегментах, то все адреса могут содержаться в одном
16-битовом значении, являющимся соглашением по использованию ре-
гистра или стека. Второе преимущество заключается в том, что прог-
рамма становится более общей, поскольку указание другого адреса
создает новый набор данных. Третье преимущество состоит в том, что
вызываемая программа может непосредственно манипулировать данными
для возврата значения в то же самое место вызывающей программы,
которое содержало первоначальное значение. Если данные, подлежащие
передаче, не размещены в памяти (т.е. являются непосредственными
данными), иногда могут возникнуть проблемы. В этих случаях (или,
если обнаружено их простое несоответствие для передачи всех требу-
емых адресов в стек) может быть использован тип смешанного пара-
метра: блок аргументов.
Блок аргументов или параметров является специальной формой пе-
редачи по адресу. В этом случае требуемые аргументы содержатся в
непрерывном участке памяти. Однако, в отличие от передачи через
общую область памяти, вызывающая процедура не имеет полных сведе-
ний об этом блоке. При вызове процедуры в качестве параметра ей
передается адрес этого блока. Хотя может оказаться неудобным раз-
мещать все требуемые аргументы в блоке, но это дает возможность
избежать необходимости размещения всех этих значений в стеке. Если
блок уже существует для других целей, то передача параметров через
блок аргументов имеет еще больший смысл.
Защита целостности передаваемых данных
Имеется другой аспект опции "передача по ...", который являет-
ся очень важным и облегчающим использование. Этот аспект относится
к целостности данных или к их защите от непреднамеренного измене-
ния или порчи. При типовом использовании данные, передаваемые по
значению,являются копией действительных данных. Как таковая, вызы-
ваемая программа может манипулировать данными по любому пути без