Способ 3, самый частый. ЧЕРЕЗ СТЕК
ИДЕЯ:
1. ПЕРЕД ВЫЗОВОМ
занести параметры в стек;
2. Вызвать процедуру;
3. Запомнить место в стеке через регистр BP (Base Pointer);
4. Внутри процедуры использовать параметры,
адресуясь ОТНОСИТЕЛЬНО BP;
5. Перед выходом восстановить
старое BP;
6. Выйти из процедуры
7. Очистить стек.
ОГРАНИЧЕНИЕ: Заносить в стек (PUSH) и читать из него (POP)
можно ТОЛЬКО МАШИННЫЕ СЛОВА (по 2 байта каждое).
ВОЗВРАТ ЗНАЧЕНИЙ ЧЕРЕЗ СТЕК
Это состояние стека сразу после возврата из процедуры. ГДЕ ТУТ МОЖЕТ БЫТЬ МЕСТО ДЛЯ ЧИСЛА, КОТОРОЕ ПРОЦЕДУРА ОСТАВИЛА В СТЕКЕ ДЛЯ ВЫЗЫВАЮЩЕЙ ПРОГРАММЫ?
ПРОЦЕДУРА обращается к этому полю как [BP + 8]
ВЫЗЫВАЮЩАЯ ПРОГРАММА обращается к этому полю по адресу SS:SP То есть может прочесть его POP reg / mem
ПОРЯДОК ЗАНЕСЕНИЯ ПАРАМЕТРОВ В СТЕК
Procedure AnyProc(x:integer; y:word; z: Pstring);
Begin … … … end;
Чем отличается А от Б?
X ßà [BP+4] или X ßà [BP+8] ?
Компилятор должен ЗАРАНЕЕ знать порядок передачи параметров!
КТО ЧИСТИТ СТЕК?
Вариант 1й чистки стека. Это случай, когда стек чистит САМА ВЫЗВАННАЯ ПРОЦЕДУРА.
.model small .stack 100h .data ; пусто. Данные программе не нужны. .code eingang: ;mov ax, @data ; здесь это ;mov ds, ax ; даже не нужно... mov dl, 'f' ; готовим параметр mov dh,0 ; для передачи ч-з стек SUB SP, 2; занять в стеке место ; под возвращаемое. push DX ; занесли параметр в стек call UPCASE ; 'F' <- 'f', результат в стеке pop DX ; забираем из стека результат. mov ah, 2 ; символ уже в DL! int 21h ; выводим его на экран mov ah,8 int 21h ; Press any key... mov ah, 4ch int 21h ; см следующую колонку | ;===Процедура - "функция" ======= UPCASE proc push BP ; пролог mov BP, SP mov ax, [BP+4] ; читаем параметр sub AL, ‘f'-'F' ; преобр в верхн регистр mov [BP+6], AX ; запись в стек ; возвращаемого pop BP ; эпилог ret 2 ; выход и чистка стека ; от одного слова (2 байта) endp ;============================= end eingang |
Вариант чистки стека №2. стек чистит ВЫЗВАЮЩАЯ ПРОЦЕДУРА.
.model small .stack 100h .data ; пусто. Данные программе не нужны. .code eingang: ;mov ax, @data ; здесь это ;mov ds, ax ; даже не нужно... mov dl, 'f' ; готовим параметр mov dh,0 ; для передачи ч-з стек SUB SP, 2; занять в стеке место ; под возвращаемое. push DX ; занесли параметр в стек call UPCASE ; 'F' <- 'f', результат в стеке ADD SP, 2 ; чистка стека от ; одного параметра (2 байта). pop DX ; забираем из стека результат. mov ah, 2 ; символ уже в DL! int 21h ; выводим его на экран mov ah,8 int 21h ; Press any key... mov ah, 4ch int 21h ; см следующую колонку | ;===Процедура - "функция" ======= UPCASE proc push BP ; пролог mov BP, SP mov ax, [BP+4] ; читаем параметр sub AL, ‘f'-'F' ; преобр в верхн регистр mov [BP+6], AX ; запись в стек ; возвращаемого pop BP ; эпилог ret ; выход endp ;============================= end eingang |
ПАРАМЕТР «ЯЗЫК» ДЛЯ ВЫЗОВА ПРОЦЕДУР
.MODEL SMALL, «язык»
Значение параметра «ЯЗЫК» | ПРОСМОТР СПИСКА ПАРАМЕТРОВ | КТО ЧИСТИТ СТЕК |
C (C++) | ß СПРАВА НАЛЕВО | Кто ВЫЗВАЛ |
PASCAL | СЛЕВА НАПРАВО à | Процедура |
STDCALL | ß СПРАВА НАЛЕВО | Процедура |
SYSCALL | ß СПРАВА НАЛЕВО | Кто ВЫЗВАЛ |
ЛОКАЛЬНЫЕ ПЕРЕМЕННЫЕ В ПРОЦЕДУРАХ
Это место создается в прологе:
. . .
PUSH BP
MOV BP, SP
SUB SP, 4 ; по 2 байта на переменную,
; SP смещается с «1» на «2»
Но тогда ЭПИлог становится такой:
. . .
MOV SP, BP ; SP смещается с «2» на «1»
POP BP
RET