- •Лекция 21
- •Процедуры
- •Процедуры
- •Процедуры
- •Процедуры
- •Описание процедуры
- •Описание процедуры
- •Описание процедур в различных сегментах
- •Описание процедур
- •Описание процедур
- •Передача параметров
- •Передача параметров через регистры
- •Пример
- •Пример
- •Пример
- •Пример
- •Передача параметров через общую память
- •Пример
- •Пример
- •Пример
- •Передача параметров через стек
- •Передача параметров через стек
- •Пример
- •Пример
- •Пример
- •Передача параметров через стек
- •Возврат значений из процедуры
- •Возврат результата
- •Локальные переменные
- •Пример
- •;Процедура рисования линии из символов.
Передача параметров через стек
Этот способ наиболее часто используется на практике. Суть этого способа заключается в том, что вызывающая процедура самостоятельно заносит в стек передаваемые данные, после чего производит вызов вызываемой процедуры.
Вызываемая процедура читает эти значения из стека.
Для работы со стеком используются три регистра:
ss – сегмент стека,
(e)bp – регистр базы,
(e)sp – текущее смещение в стеке.
Передача параметров через стек
При таком способе передачи код процедуры несколько изменяется:
MyProcPROC |
|
|
push |
bp |
;Настройка базы |
mov |
bp, sp |
|
… |
|
|
pop |
bp |
;Восстановление базы |
ret |
|
|
MyProcENDP |
|
|
В таком случае: bp – адрес стека на момент передачи управления вызываемой процедуре. По этому адресу располагается старое значение bp.
Пример
.486
model small
Data SEGMENT use16
ASSUME ds:Data
str1 |
db |
'It is first string for test!$' |
str2 |
db |
'It is second string!$' |
Data ENDS |
|
|
Stk SEGMENT use16 stack |
||
db |
256 dup(0) |
Stk ENDS
Пример
Code SEGMENT use16
ASSUME cs:Code
StrLen |
PROC |
push bp |
|
mov |
bp, sp |
xor |
ah,ah |
mov |
si, [bp+4] ;Чтение адреса строки |
next: |
|
lodsb |
|
cmp |
al,'$' |
jz |
finprc |
inc |
ah |
jmp |
next |
finprc: |
|
shr |
ax, 8 |
pop |
bp |
ret |
2 |
StrLen |
ENDP |
PrintLen PROC
push |
bp |
mov |
bp, sp |
mov |
cx, 0b800h |
mov |
es, cx |
mov |
ax, [bp+4] ;Чтение позиции |
mov |
cl, 160 |
mul |
cl |
mov |
di, ax |
mov |
ax, [bp+6] ;Чтение длины строки |
mov |
cl, 10 |
div |
cl |
mov |
cx, ax |
mov |
ah, 15 |
add |
al, 48 |
mov |
es:[di],ax |
mov |
al, ch |
add |
al, 48 |
mov |
es:[di+2], ax |
pop |
bp |
ret |
|
PrintLen ENDP
Пример
start: |
mov |
ax, Data |
mov |
ds, ax |
|
mov |
ax, offset str1 |
|
push |
ax |
;Запись в стек адреса строки str1 |
call |
StrLen |
|
push |
ax |
;Запись в стек длины строки str1 |
push |
word ptr 1 ;Запись в стек позиции |
|
call |
PrintLen |
|
add |
sp, 4 |
|
mov |
ax, offset str2 |
|
push |
ax |
;Запись в стек адреса строки str2 |
call |
StrLen |
|
push |
ax |
;Запись в стек длины строки str1 |
push |
word ptr 5 ;Запись в стек позиции |
|
call |
PrintLen |
|
wait0: |
in |
al, 60h |
cmp |
al, 1 |
|
jnz |
wait0 |
|
mov |
ax, 4c00h |
|
int |
21h |
|
Code ENDS end start
Передача параметров через стек
При завершении процедуры необходимо осуществить изъятие переданных параметров из стека. Это можно сделать следующими способами:
используя последовательность команд pop (не рекомендуется);
откорректировать регистр указателя на вершину стека, например с помощью команды add;
используя машинную команду ret n в качестве исполняемой команды в процедуре, где n – количество байт на которое необходимо увеличить содержимое регистра (e)sp.
Возврат значений из процедуры
Возврат значений может осуществляться:
через регистры,
через общую область памяти,
через стек.
При реализации функций на языках программирования высокого уровня возвращаемое значение, если оно приводится к целочисленному значению, передается через регистр EAX или его часть (AL, AX) или регистровую пару EDX:EAX.
Если результат является вещественным значением, то он возвращается через вершину стека сопроцессора.
Возврат результата
Возврат через регистры: ограничение в этом способе те же, что и передаче значения: небольшое количество регистров и их фиксированный размер.
Возврат через общую область: проблемы те же, что и при передаче значений.
Возврат через стек: также используется регистр (e)bp. Возможны два варианта:
использование для возвращаемых аргументов тех же ячеек в стеке, которые использовались для передачи аргументов в процедуру;
предварительное помещение в стек наряду с передаваемыми аргументами фиктивных аргументов с целью резервирования места для возвращаемого значения.
Локальные переменные
Локальныефункциипеременные функции создаются в стеке. Для этого в начале процедуры указатель стека уменьшается на значение равное количеству байт занимаемых локальными переменными в памяти. В конце процедуры указатель стека увеличивается на соответствующее количество байт.
Адресация при обращении к локальным переменным осуществляется относительно значения регистра (E)BP в сторону младших адресов.
MyProc |
PROC |
|
|
push |
bp |
|
mov |
bp, sp |
|
sub |
sp, размер_блока_локальных_данных |
|
… |
|
|
add |
sp, размер_блока_локальных_данных |
|
pop |
bp |
|
ret |
|
MyProc |
ENDP |
|
|
|
|
Пример
Написать процедуру для вывода сообщения в рамке и протестировать её работу, выведя несколько сообщений. В качестве параметра ей будет передаваться адрес строки в регистре BX. Строка должна заканчиваться символом ‘$’. Для упрощения процедуры можно разбить задачу на подзадачи и написать соответствующие процедуры. Прежде всего нужно вычислить длину строки, чтобы знать ширину рамки. Процедура get_length вычисляет длину строки (адрес передаётся также в BX) и возвращает её в регистре AX.
Для рисования горизонтальной линии из символов предназначена процедура draw_line. В DL передаётся код символа, а в CX — количество символов, которое необходимо вывести на экран. Эта процедура не возвращает никакого значения. Для вывода 2-х символов конца строки написана процедура print_endline. Она вызывается без параметров и тоже не возвращает никакого значения.
use16 |
;Генерировать 16-битный код |
|
org 100h |
;Программа начинается с адреса 100h |
|
jmp start ;Переход на метку start |
||
;---------------------------------------------------------------------- |
|
|
msg1 db 'Hello!$' |
|
|
msg2 db 'asmworld.ru$' |
|
|
msg3 db 'Press any key |
...$' |
|
;---------------------------------------------------------------------- |
|
|
start: |
|
|
mov bx,msg1 |
|
|
call print_message |
;Вывод первого сообщения |
|
mov bx,msg2 |
|
|
call print_message |
;Вывод второго сообщения |
|
mov bx,msg3 |
|
|
call print_message |
;Вывод третьего сообщения |
|
mov ah,8 |
|
;Ввод символа без эха |
int 21h |
|
|
mov ax,4C00h |
|
;\ |
int 21h |
|
;/ Завершение программы |
;---------------------------------------------------------------------- |
|
|
|
|
|
;----------------------------------------------------------------------
;Процедура вывода сообщения в рамке ;В BX передаётся адрес строки
print_message: |
|
push ax |
;Сохранение регистров |
push cx |
|
push dx |
|
call get_length |
;Вызов процедуры вычисления длины строки |
mov cx,ax |
;Копируем длину строки в CX |
mov ah,2 |
;Функция DOS 02h - вывод символа |
mov dl,0xDA |
;Левый верхний угол |
int 21h |
|
mov dl,0xC4 |
;Горизонтальная линия |
call draw_line |
;Вызов процедуры рисования линии |
mov dl,0xBF |
;Правый верхний угол |
int 21h |
|
call print_endline |
;Вызов процедуры вывода конца строки |
mov dl,0xB3 |
;Вертикальная линия |
int 21h |
|
mov ah,9 |
;Функция DOS 09h - вывод строки |
mov dx,bx ; |
Адрес строки в DX |
int 21h |
|
|
|
mov ah,2 |
;Функция DOS 02h - вывод символа |
mov dl,0xB3 |
;Вертикальная линия |
int 21h |
|
call print_endline |
;Вызов процедуры вывода конца строки |
mov dl,0xC0 |
;Левый нижний угол |
int 21h |
|
mov dl,0xC4 |
;Горизонтальная линия |
call draw_line |
|
mov dl,0xD9 |
;Правый нижний угол |
int 21h |
|
call print_endline |
;Вызов процедуры вывода конца строки |
pop dx |
;Восстановление регистров |
pop cx pop ax
ret ;Возврат из процедуры
;----------------------------------------------------------------------
;Процедура вычисления длины строки (конец строки - символ '$'). ;В BX передаётся адрес строки.
;Возвращает длину строки в регистре AX. get_length:
push bx xor ax,ax
str_loop:
cmp byte[bx],'$' je str_end
inc ax inc bx
jmp str_loop str_end:
pop bx ret