Процедуры в ассемблере.
Процедуры в ассемблере будут рассмотрены в четырёх статьях, в которых мы изучим общие понятия и определения процедур, использование стека для передачи параметров, а также использование прерываний DOS — как разновидности функций ядра операционки (статьи 15-19: «Процедуры (функции)», «Стек», «Конвенции вызова функции», «Упрощаем вызов функции в TASM», «Прерывания DOS»).
Начнём изучать функции на примере нашей программы goblin.com. Сразу определимся, что понятия: процедура, функция, подпрограмма в языках программирования, включая ассемблер, являются синонимами и обозначают одно и то же. Именно в качестве равнозначных синонимов мы будем использовать эти названия.
Изучаем процедуры на примере goblin.com.
Пакет всего необходимого, включая исходники (DOS-1.rar) можно скачать с нашего сайта по ссылке.
Полный код нашей подопытной программы:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 | ;goblin.asm .model tiny ; for СОМ .code ; code segment start org 100h ; offset in memory = 100h (for COM) start: main proc begin: mov ah,09h mov dx,offset prompt int 21h inpt: mov ah,01h int 21h cmp al,'m' je mode_man cmp al,'w' je mode_woman call goblin jmp begin mode_man: mov addrs,offset man; указатель на процедуру в addrs jmp cont mode_woman: mov addrs,offset woman; указатель на процедуру в addrs cont: call word ptr addrs; косвенный вызов процедуры mov ax,4c00h int 21h main endp man proc mov ah,09h mov dx,offset mes_man int 21h ret man endp woman proc mov ah,09h mov dx,offset mes_womn int 21h ret woman endp goblin proc mov ah,09h mov dx,offset mes_gobl int 21h ret goblin endp ;DATA addrs dw 0;for procedure adress prompt db 'Are you Man or Woman [m/w]? : $' mes_man db 0Dh,0Ah,"Hello, Strong Man!",0Dh,0Ah,'$' ; строка для вывода. Вместо ASCII смвола '$' можно написать машинный код 24h mes_womn db 0Dh,0Ah,"Hello, Beautyful Woman!",0Dh,0Ah,'$' ; строка для вывода mes_gobl db 0Dh,0Ah,"Hello, Strong and Beautyful GOBLIN!",0Dh,0Ah,24h ; строка для вывода. 24h = '$' . len = $ - mes_gobl end start |
Goblin.com включает в себя несколько подпрограмм:
- main proc
- man proc
- woman proc
- goblin proc
Каждая подпрограмма имеет определённую задачу и, будучи написанной один раз может вызываться в процессе исполнения программы неоднократно. Процедуры в ассемблере не являются обязательным элементом программы, а просто повышают её наглядность (в Си и СРР это не так). Процедура упрощает код, делает его более структурированным, сокращают его размер.
Вызов процедуры в ассемблере.
Вызов процедуры в ассемблере осуществляется командой call (call — вызов). После вызова, процедура исполняет свой код и программа возвращается в точку возврата (выполняет дальнейший код, следующий за вызовом — командой call).
Процедура может иметь свои аргументы — данные, которые ей предоставляются для обработки и результат (входные и выходные данные — in/out).
Процедура в ассемблере обозначается названием и начинается оператором proc (procedure — процедура). Заканчивается процедур оператором end proc (end procedure — конец процедуры) и командой ret (return — возврат). В главной функции (main в нашем случае) команда ret после выхода в систему DOS не обязательна.
имя_процедуры proc
…
тело процедуры (код)
…
ret
endp
Для удобной читаемости кода можно указывать имя (название) соответствующей процедуры также перед оператором endp.
имя_процедуры proc
…
тело процедуры (код)
…
ret
имя_процедуры endp
Необходимо понимать, что фактически имя процедуры в ассемблере является указателем на процедуру. Это позволяет организовать прямой и косвенный вызовы процедуры (адрес процедуры можно поместить в регистр, записать в блок памяти).
Работа с процедурами на примере программы goblin.com.
Процедуры man proc, woman proc, goblin proc идентичны (на примере woman proc):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | woman proc mov ah,09h mov dx,offset mes_womn int 21h ret woman endp ... cmp al,'m' je mode_man cmp al,'w' je mode_woman call goblin jmp begin mode_man: mov addrs,offset man; указатель на процедуру в addrs jmp cont mode_woman: mov addrs,offset woman; указатель на процедуру в addrs cont: call word ptr addrs; косвенный вызов процедуры ... addrs dw 0;для указателя на процедуру |
Вызов процедур goblin — прямой:
1 | call goblin |
Вызов процедур woman и man косвенный (через указатель):
1 2 3 4 5 6 | ... mov addrs,offset man; указатель на процедуру в addrs ... mov addrs,offset woman; указатель на процедуру в addrs ... call word ptr addrs; косвенный вызов процедуры |
В организации работы процедуры задействованы команды CALL, RET, а также стек.
CALL (CALL).
Вызов процедуры с запоминанием в стеке точки возврата:
call адрес перехода.
Примеры для CALL:
1 2 3 4 5 6 7 8 9 10 11 12 13 | woman proc mov ah,09h;аргумент для прерывания int 21h. 09h в ah - вывод на экран, адрес выводимой строки - в dx mov dx,offset mes_womn; адрес выводимой строки int 21h;вызов прерывания ret; woman endp ... call woman; прямой вызов процедуры mov addrs,offset woman; указатель на процедуру в addrs call word ptr addrs; косвенный вызов процедуры ... addrs dw 0; для указателя на процедуру mes_womn db 0Dh,0Ah,"Hello, Beautyful Woman!",0Dh,0Ah,'$' ; строка для вывода |
RET (RETurn).
Возврат из процедуры обратно к месту вызова.
В действительности микропроцессор имеет три вида команды ret:
- ret и retn — возврат из процедур ближнего типа
- retf — возврат из процедур дальнего типа.
Выбор сделает транслятор, поэтому достаточно использовать ret.
Отдельные языки программирования требуют, чтобы процедура очищала стек от переданных параметров. Поэтому команда ret имеет необязательный параметр — число, который обозначает количество байт или слов (в зависимости от установленного атрибута размера адреса), удаляемых из стека по окончанию работы процедуры.
Примеры для RET:
1 2 3 4 5 6 7 8 9 10 11 | woman proc mov ah,09h;аргумент для прерывания int 21h. 09h в ah - вывод на экран, адрес выводимой строки - в dx mov dx,offset mes_womn; адрес выводимой строки int 21h;вызов прерывания ret; woman endp my_proc proc ... ret 8 my_proc endp |
Необходимо отметить, что процедура может иметь параметры, через которые ей передаются данные, а также через которые она передаёт возвращаемые значения. Это может быть реализовано через регистры (аналогичный метод используют знакомые нам прерывания), а также через указатели на выделенные участки памяти (в нижеприведённом примере: mov dx,offset mes_womn; адрес выводимой строки).
Система передачи данных системных функций Windows, а также принятые способы реализации процедур современных операционок будут рассмотрены в цикле статей 32-битного и 64-битного программирования.
В следующей статье мы поговорим о стеке, который непосредственно связан с работой процедур, рассмотрим способы передачи процедуре параметров, выводе результатов.