39. Способы передачи параметров в процедуры

Через регистры
Перед вызовом процедуры необходимые параметры сохраняются в регистрах. Такой метод является самым быстрым, но есть существенный недостаток: количество регистров ограничено.

mov        ax,value   ; сохраняем значение нашей переменной в регистре ах
call       procedure           ; вызываем процедуру



Через внешние имена
Имя считается определенным, если оно использовалось в поле оператора или псевдооператора.
Имя считается внешним, если оно объявлено в одном сегменте, а использовалось в другом.
Директива PUBLIC имя[, имя…] делает имя доступным для других процедур
Директива EXTRN имя:тип [, имя:тип…] указывает, что имя является внешним


Типы передаваемых параметров
Параметр Тип
Переменная DB, DW, DT и т.д
Константа ABS
Метка или Процедура NEAR/FAR

Пример: объявление внешних и открытых имен

Файл_1
….
Public x, y; делаем переменную х и метку у доступными для других процедур
….
.data
x dw 5 ;объявляем переменную
….
.code
….
y label far; объявляем метку

Файл_2
extrn x: word y: far; объявляем, что параметры х и у, которые мы будем использовать далее, являются внешними

Через общую область
Вызывающая и вызываемая процедура «договариваются» о совместном использовании некоторой области памяти.
Сегменты данных при этом должны иметь одинаковые имена и тип комбинирования common

Таким образом сегменты «накладываются»: переменные находящиеся на одинаковых местах, даже если у них разные имена, позволяют обращаться к одним и тем же данным. Важно помнить, что изменения этих данных внутри процедуры ведет к изменению их в главной программе, так что если вы не планируете менять значения параметра его надо заранее сохранить и восстановить или же в подпрограмме пользоваться локальной копией.

Пример: объявление сегментов данных для использования общей области памяти
File 1
Comdata segment para common ‘data’
Text db 15 dup(‘ ’)
Len dw ?
Ptr dw text
Comdata ends

File 2
Comdata segment para common ‘data’
mas db 15 dup(‘ ’)  ;соответствует Text в File_1
Count dw ?              ; Len
pmas dw text          ; Ptr
elem db 0                 ; не имеет аналогов в File_1
Comdata ends

Через таблицу параметров
В основном сегменте данных выделяется дополнительная память – таблица параметров. В эту таблицу перед вызовом процедуры заносятся значения параметров, а в подпрограмму передается только адрес таблицы (например через регистр).

Пример: вычисление сумы элементов массива:
…
.data
A dw 2, 3, -7, 4         ;объявляем массив, элементы которого будем суммировать
N dw ($ - A)/type A ; определяем количество элементов в массиве
S dw ?                        ; в этой переменной будет храниться сума
T dw 3 dup (?)         ; таблица параметров
main  proc FAR
Lea bx, A            ;получаем адрес массива
Mov T, bx           ;записываем его в таблицу на первое место
Mov bx, N          
Mov T+2, bx      ;записываем в таблицу количество переменных на второе место
Lea bx, S          
Mov T+4, bx      ;записываем в таблицу на третье место адрес места, где будет храниться сума
Lea bx, T             ;загружаем адрес таблицы в регистр bx
Сall Sum             ;вызываем процедуру суммирования

Sum proc NEAR ;объявляем процедуру подсчета сумы элементов массива
Push ax  ;сохраняем значения нужных нам регистров в стеке
Push cx
Push si
Push di
Xor ax, ax  ;обнуляем регистр ах
Mov si, [bx]  ;загружаем в si адрес массива
Mov cx, [bx+2]  ;загружаем в сх количество элементов массива
Mov di, [bx+4]   ;загружаем в di адрес ячейки для сумы
M1: add ax, [si] ;собственно подсчет
         add si, 2
loop M1
mov [di], ax  ;записываем полученную суму на свое место
pop di ;восстанавливавшем регистры
pop si
pop cx
pop dx
RET ;возврат из процедуры
Sum ENDP

Через стек
Перед вызовом процедуры все параметры заносятся в стек. Размещенные параметры удаляются из стека внутри вызываемой или вызывающей процедуры, так же можно использовать директиву RET n.
Что делать, если подпрограмма и сама активно использует стек? Для того что бы иметь доступ к сохраненным в стеке аргументам, необходимо в начале вызываемой процедуры сохранить в регистре bp адрес вершины стека для этого пишут пролог:


Push bp
Mov bp, sp

Теперь можно обращаться в аргументам в стеке относительно адреса хранящегося в bp. Важно при этом помнить, что команда вызова процедуры САLL поместит на верхушку стека значение ip или ip и cs в зависимости от типа вызываемой подпрограммы.
Для восстановления состояния стека до его использования подпрограммой, необходимо загрузить в регистр sp адрес хранящийся в bp (написать эпилог):

Mov sp, bp
Pop bp

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

;сегмент данных выглядит точно также, только таблица нам уже не нужна
.data
A dw 2, 3, -7, 4         ;объявляем массив, элементы которого будем суммировать
N dw ($ - A)/type A ; определяем количество элементов в массиве
S dw ?                        ; в этой переменной будет храниться сума

Mаin  proc FAR
Lea bx, A            ;получаем адрес массива
Push bx           ;записываем его стек
Mov bx, N          
Push bx      ;записываем в стек количество переменных 
Lea bx, S          
Push bx      ;записываем в стек адрес места, где будет храниться сума
Сall Sum             ;вызываем процедуру суммирования

Sum proc NEAR ;объявляем процедуру подсчета сумы элементов массива
Push bp         ;две строчки - пролог
Mov bp, sp
Pusha  ; теперь можем смело пользоваться стеком - сохраняем значения нужных регистров
Xor ax, ax  ;обнуляем регистр ах
Mov si, [bp + 8]  ;загружаем в si адрес массива 
;при этом помним, что элементы у нас размером слово следовательно занимают они по два байта и размещены в обратном порядке – чем раньше мы элемент запихнули, тем дальше он находиться
Mov cx, [bp + 6] ;загружаем в сх количество элементов массива
Mov di, [bp + 4]  ;загружаем в di адрес ячейки для сумы
M1: add ax, [si] ;собственно подсчет
         add si, 2
loop M1
mov [di], ax  ;записываем полученную суму на свое место
popa ;восстанавливаем регистры
mov sp, bp ;две строчки эпилога
pop bp
RET 6; возврат из процедуры – заодно выталкиваем наши параметры
Sum ENDP
17.07.2015