Команда вызова процедур CALL
Команда CALL (вызов) аналогична команде JMP. Поскольку команда CALL предназначена
для вызова процедур, дополнительно она запоминает в стеке еще и адрес
точки возврата.
Синтаксис:CALL Имя_процедуры
Логика работы команды в случае Near-вызова процедуры:
PUSH IP
IP= IP + смещение_к_нужной_процедуре
Логика работы команды в случае Far-вызова процедуры:
PUSH CS
PUSH IP
CS=Code2
IP= IP + смещение_к_нужной_процедуре
Если у вызываемой процедуры есть параметры, они передаются через стек ДО вызова
процедуры.
Для облегчения передачи параметров можно использовать команду CALL в
расширенном синтаксисе: CALL Имя_процедуры [язык[, арг1]...]
,
где язык - это С, СРР, PASCAL, BASIC, FORTRAN,
PROLOG, NOLANGUAGE,
арг — аргумент, помещаемый в стек в
соответствии с принятыми соглашениями.
Команда возврата в точку вызова RET
Команда RET (RETurn from procedure — возврат из процедуры) по действию
является обратной команде CALL. Она обеспечивает возврат управления вызывающей
программе и, если нужно, очистку стека на величину 16-разрядной константы [Iml6] байт.
Синтаксис:RET [Iml6]
Логика работы команды в случае Near-вызова процедуры:
POP IP
Логика работы команды в случае Far-вызова процедуры:
POP IP
POP CS
Соглашения о вызовах в стиле C/C++
Параметры в стек передаются справа налево.
Первым в стек помещается ПОСЛЕДНИЙ параметр, последним — ПЕРВЫЙ.
По окончании работы очистить стек должна ВЫЗЫВАЮЩАЯ процедура.
; x=(2*a + b*c)/(d-a)
; int x,a,b,c,d;
CODESEG
Extrn C X1:Dword
Extrn C X2:Dword         
                        
    ; Глобальные переменные
Public C prim
; int prim (int a, int b, int c, int d,);
prim proc far
push bp
mov bp, sp          
                        
                   ; указатель bp -
на вершину стека
         
                        
                        
                      ; !!!
параметры в стеке хранятся в ОБРАТНОМ порядке
CS EQU [bp+2] IP equ [bp+4]
a EQU [bp+6]
b EQU [bp+8]
c EQU [bp+10]
d EQU [bp+12]
mov ax,2
imul a          
                        
                        
        ; < dx >:< ax > = 2*a
mov bx,dx
         
                        
                      ; bx <==
ст. часть (dx)
mov cx,ax          
                        
                       ; cx <==
мл. часть (ax)
mov ax,b
imul c         
                         
                         
        ; < dx >:< ax > = b*c
add ax, cx
                          
                           
    ; < ax > = < ax > + < cx > (мл. часть)
adc dx, bx
                           
                          
    ; < dx > = < dx > + < bx >(ст. часть)
mov word PTR X1, ax
mov word PTR X1+2, dx               
            ; числитель
mov cx, d
sub cx, a
                       
                           
          ; < cx > = < cx > -a
mov X2,cx
                          
                           
    ; знаменатель
idiv cx          
                        
                        
        ; < ax > = < dx > : < ax > / < cx >
pop bp
ret
prim endp
end
Модель large означает, что все процедуры имеют атрибут FAR.
Функция int prim (int a, lilt b, int с, int d) вызывает из С++.
ДО вызова функции в стек должны быть переданы параметры в обратном порядке
(справа налево: d,c,b,a):
О передаче параметров заботится программа на С++.
Затем следует ДАЛЬНИЙ вызов функции: CALL prim
.
При этом в стек загружается адрес возврата в точку вызова:
PUSH IP
PUSH CS
Затем начинает выполняться функцию, реализованную на Ассемблере. Вначале
устанавливаем указатель на вершину стека. Для этого используется регистр BP:
push bp
mov bp,sp
Содержимое стека после вызова подпрограммы prim:
Положение вершины стека |
Содержимое стека (длина ячейки 16 бит) |
Значение смещения относительно регистра BP |
SP |
??? |
|
|
BP |
|
|
CS |
+2 |
|
IP |
+4 |
|
a |
+6 |
|
b |
+8 |
|
c |
+10 |
|
d |
+12 |
В конце подпрограммы происходит восстановление значения регистра BP и
возврат в точку вызова. Очистка стека здесь НЕ делается, этим должна заниматься
программа на С++. Директива EQU применяется для удобства
сопоставления смещения в стеке с реальными параметрами.