ИУИКСИС Лаб. раб. / 3 / laba_ass_03
.docЛабораторная работа
Использование макросов в ассемблерных программах
Использование макросов позволяет упростить процесс программирования и существенно улучшить «читабельность» программ.
Целью лабораторной работы является изучение некоторых полезных макросов из файла macros.inc, способов их использования и приобретение навыков в написании и отладке ассемблерных программ.
Конкретные задания на работу выделены в тексте курсивом.
-
Макроопределения
Макроопределение (макрос) имеет следующую структуру:
<Имя макроса> MACRO [<Формальные параметры через запятую>]
[LOCAL <Перечень локальных меток>]
<Тело макроса>
ENDM,
в […] – скобках указаны необязательные параметры.
Макросы, также как и процедуры (функции) пишутся для повторного использования и размещаются в библиотеках. В отличие от процедур (функций), макроопределения представляют из себя «шаблоны», по которым строится программный код только в процессе компиляции. Программный код вставляется на месте макровызова, где программист указывает имя нужного макроса и его фактические параметры. Во время компиляции все формальные параметры заменяются на фактические. Использование макросов может заметно увеличивать размер программного кода, зато при макровызовах не происходит передачи управления и запоминания точки возврата в стеке.
Ниже приведены примеры некоторых полезных макросов из MACROS.INC.
-
Ввод символа с клавиатуры
Приведенный ниже макрос GetChar используется в программах, когда надо обеспечить ввод только одного символа с клавиатуры.
GetChar macro
mov ah,01h ; функция DOS 01h – ввод символа
int 21h
endm
Как видно из его описания, макрос не имеет формальных параметров и локальных меток. Эти параметры не являются обязательными. Тело макроса образуют две команды. Первая mov ah,01h задает номер функции DOS в регистре ah (функция 01h - ввод символа с клавиатуры), а вторая int 21h вызывает подпрограмму выполняющую эту функцию. Подпрограмма построена так, что она ожидает ввода и, как только будет нажата любая клавиша, соответствующий ей код символа будет размещен в регистре al.
-
Сохранение и восстановление регистров
При написании подпрограмм и макросов полезно все регистры процессора, которые используются в подпрограмме (или макросе) вначале сохранить в стеке, а затем перед выходом из подпрограммы (или перед окончанием макроса) – восстановить. Это будет гарантией того, что программисту, использующему в дальнейшем подпрограмму (макрос) не надо будет заботиться о сохранении «своих» регистров.
Для целей сохранения регистров удобно использовать макрос SaveReg, приведенный ниже. В качестве формальных параметров здесь указан список регистров RegList.
SaveReg macro RegList
irp reg,<RegList>
push reg
endm
endm
При использовании этого макроса в программе список регистров должен быть заключен в угловые скобки, например - SaveReg <ax,bx,cx>. На первый взгляд может показаться, что директива endm ошибочно написана дважды. Но это не так. Первый раз endm «закнчивает» макрос irp reg,<RegList>, авторой раз – основной макрос SaveReg.
Для целей восстановлени регистров удобно использовать макрос LoadReg, приведенный ниже. В качестве формальных параметров здесь указан список регистров RegList.
LoadReg macro RegList
irp reg,<RegList>
pop reg
endm
endm
При использовании этого макроса в программе список регистров должен быть заключен в угловые скобки, например - LoadReg <cx,bx,ax>, причем обратите внимание на то, что имена регистров должны следовать в порядке обратном тому, в каком они сохранялись.
-
Переход на следующую строку
Для перевода курсора на экране дисплея на следующую строку надо выполнить два действия: вывести код символа возврата каретки и перевода строки. Макрос NextLine выполняет эти действия с использованием соответствующей функции DOS.
NextLine macro
SaveReg <ax,dx>
mov ah,02h ; функция DOS 02h – вывод символа
mov dl,13 ; CR – возврат каретки
int 21h
mov dl,10 ; LF – перевод строки
int 21h
LoadReg <dx,ax>
endm
Обратите внимание на то, что для сохранения регистров, используемых в макросе NextLine, используются макросы SaveReg и LoadReg, описанные в предыдущем разделе.
-
Ввод строки произвольной длины
Для ввода с клавиатуры последовательности символов произвольной длины (до 256) используется макрос GetStr, который имеет два формальных параметра – Buf адрес строки, куда будут помещены введенные символы и MaxLen максимальная длина вводимой строки (указывает программист).
Результатом работы макроса после его макровызова будет введенная строка символов по адресу Buf и длина введенной строки в регистре АХ.
GetStr macro Buf,MaxLen
local m,TmpBuf
jmp m
TmpBuf label byte ; временный буфер для функции DOS 0ah
rept MaxLen+3 ; доп. три байта - служебная информация
db ' '
endm
m:
SaveReg <ds,es,dx,cx>
xor cx,cx
mov cs:TmpBuf,MaxLen+1
mov ah,0ah
push ds
pop es
push cs
pop ds
lea dx,cs:TmpBuf
int 21h
mov al,cs:TmpBuf+1
; Пересылка TmpBuf в Buf (для удаления служебных символов):
mov cl,al ; длина введенной строки в al
xor ah,ah ; 0 в ah
lea si,cs:TmpBuf+2 ; откуда - ds:si
lea di,buf ; куда - es:di
rep movsb
LoadReg <cx,dx,es,ds>
endm
1.5 Вывод строки на экран
Для вывода произвольной строки на экран дисплея используется макрос. Макрос имеет один формальный параметр, который при макровызове должен быть заменен на фактический. Для правильной работы макроса необходимо, чтобы выводимая строка заканчивалась символом '$' – признак конца строки в ассемблере.
OutStr macro str
SaveReg <ax,dx>
mov ah,09h
lea dx,str
int 21h
LoadReg <dx,ax>
endm
Задание 1.
Внимательно ознакомьтесь с макросами 1.1 - 1.5. Используя необходимые макросы, напишите программу, которая ожидает ввод строки (конец ввода <Enter>). По окончании ввода программа должна вывести полученную строку на экран, предварительно осуществив перевод строки (т.е. в результате получить две одинаковые строки друг под другом).
; Прототип программы с использованием модели small
include macros.inc ; подключение библиотеки макросов
.model small
.stack 100h
.data
string db 256 dup (‘$’)
.code
start:
mov ax,@data
mov ds,aх ; загрузка сегментного регистра DS
… ; здесь должен быть размещен Ваш код
…
…
mov ah,4ch ; выход из
int 21h ; программы
end start
1.6 Пересылка строк
mov_string macro dest,src,len
;На входе идентификаторы: строки-источника - src, строки-приемника - dest
;сегментные регистры ds (для источника) и es (для приемника) должны быть ; загружены правильными значениями до вызова макрокоманды
mov cx,len
lea si,src
lea di,dest
rep movsb
endm
1.7 Очистка строки произвольной длины пробелами
null_string macro dest,len
local m,Z_String
;на входе:
;dest - адрес строки
;len - длина очищаемой строки
jmp m
Z_String label byte ;пустая строка
rept len
db ' '
endm
m:
SaveReg <ds,es,cx,si,di>
mov cx,len
push ds
pop es ;адрес dest (приемник) - es:di
push cs
pop ds ;адрес Z_String (источник) - ds:si
lea si,cs:Z_String
lea di,dest
rep movsb
LoadReg <di,si,cx,es,ds>
Endm
1.8 Вывод символа на экран
OutChar macro
;На входе - в dl выводимый символ.
push ax
mov ah,02h
int 21h
pop ax
endm
1.9 Очистка регистра rg
clear_r macro rg
xor rg,rg
endm
1.10 Настройка ds на начало сегмента данных
init_ds macro
mov ax,data
mov ds,ax
xor ax,ax
endm
1.11 Корректный выход из программы
_Exit macro
mov ax,4c00h
int 21h
endm
-
Использование макроопределений в ассемблерных программах
Задание 2.
Используя результаты, полученные при выполнении первого задания, напишите программу, которая также ожидает ввод строки с клавиатуры, а по окончании ввода заменяет последовательность введенных символов на противоположную и выводит на экран.
Например, вводится строка:
QWERTY12345 <Enter>
Программа должна вывести на экран:
54321YTREWQ.
Пояснение. Для приведенного в задании 2 примера после выполнения макровызова
GetStr string,20 символы строки расположатся следующим образом: string[0] = Q
string[1] =W
string[2] =E
string[3] =R
string[4] =T
string[5] =Y
string[6] =1
string[7] =2
string[8] =3
string[9] =4
string[10] =5,
а в регистре АХ будет число введенных символов АХ=11.
Введите еще одну переменную (например. string1 db 256 dup (‘$’) ), в которую перепишите строку string, начиная с последнего символа к первому. После чего выведите string1 на экран дисплея.
Литература
1. Конспект лекций.
2. Юров В. ASSEMBLER. Учебник. «Питер», 2001.