Главная /
Ассемблер /
Для чайников /
Введение /
Процедуры в Ассемблере
Если вам доводилось программировать на языках высокого уровня, то вы знаете,
что такое подпрограммы. Если не знаете, то см. здесь.
Без использования подпрограмм крайне сложно создать какую-либо мало-мальски сложную программу. Особенно на языке ассемблера.
Вкратце об использования подпрограмм в ассемблере я рассказывал
здесь. Ну а сегодня буду говорить об этом более подробно.
Описание процедуры в Ассемблере выглядит примерно так:
Имя PROC
; здесь находится код
; процедуры ...
RET
Имя ENDP
Имя - это имя процедуры. Называйте как хотите. Только не вставляйте пробелы и другие служебные символы. Имя также не должно начинаться с цифры и должно быть уникальным. Имя должно быть одинаковым в начале и конце процедуры.
PROC и ENDP - директивы компилятора, которые указывают компилятору на адрес процедуры. Между этими словами находится код процедуры.
RET - уже знакомая нам команда, которая используется для возвращения в операционную систему, если она выполняется в программе. Если же она находится в процедуре, то она используется для возвращения в основную программу из процедуры.
Ну вот, процедура написана. Осталось вызвать ее в нужном месте программы. Для этого используется инструкция CALL, после которой пишется имя процедуры. Вот так все просто оказалось.
А теперь попробуем использовать полученные знания в примере, который мы
рассматривали здесь (без использования процедур).
В этом примере мы выводили строку на экран. Напишем две процедуры на ассемблере - некое
подобие Write и WriteLn в Паскале.
.model tiny
.code
ORG 100h ;Начало СОМ-файла
start:
MOV DX, offset stroka1 ;Адрес строки записываем в DX
CALL Write
MOV DX, offset stroka2 ;Адрес строки записываем в DX
CALL WriteLn
MOV DX, offset stroka1 ;Адрес строки записываем в DX
CALL WriteLn
MOV DX, offset stroka2 ;Адрес строки записываем в DX
CALL WriteLn
RET ;Завершение СОМ-файла
;Описание процедуры вывода строки
Write PROC
MOV AH, 09h
INT 21h
RET ;Завершение процедуры Write
Write ENDP
;Описание процедуры вывода строки с переводом строки
WriteLn PROC
MOV AH, 09h
INT 21h
MOV DX, offset crlf
MOV AH, 09h
INT 21h
RET ;Завершение процедуры WriteLn
WriteLn ENDP
stroka1 DB 'Line 1 $' ;Строка для вывода
stroka2 DB 'Line 2 $' ;Строка для вывода
crlf DB 0Dh, 0Ah, '$' ;Символы перевода строки
END start
То, что выведет программа, вы видите на рисунке:
Как видите, сначала процедура Write выводит строку “Line 1” без перевода на новую строку. Поэтому строка “Line 2” выводится в той же строке. Ну а далее мы уже используем WriteLn, поэтому следующие строки выводятся с новой строки.
А теперь представьте, как бы выглядел этот код, если бы мы не использовали процедуры - он занимал бы намного больше места.
Описание процедур в исходном коде обычно выполняется после текста основной программы, но перед объявлением переменных.
Параметры в процедуры обычно передаются через регистры и/или через глобальные переменные (как это сделано в нашем примере - через регистр DX в процедуру передаётся адрес выводимой строки, ну а сама строка объявлена как глобальная переменная).
Процедуры могут быть также вложенными одна в другую. В этом случае команда RET вложенной процедуры передаёт управление не в основную программу, а в процедуру, из которой была вызвана вложенная процедура.
Также во многих ассемблерах (например, в MASM) по
умолчанию метки являются локальными. То есть если в процедуре будет метка, то она будет видима только в пределах этой процедуры.