→ Пошук по сайту       Увійти / Зареєструватися
Знання Асемблер Основи програмування

Макрозасоби асемблера

Сучасні асемблери містять в собі так звані макрозасоби і з цієї причини називаються іноді макроасемблерами. Загальна ідея макрозасобів полягає в тому, що включенням в початковий текст програми пропозицій спеціальної мови макрозасобів (макромови) ми якоюсь мірою управляємо процесом трансляції програми. Макромова дозволяє виконувати або не виконувати трансляцію окремих ділянок програми залежно від деякого нами ж визначуваної умови (умовна трансляція); здійснювати розмноження ділянки початкового тексту програми, зокрема, з модифікацією кожного повторення (блоки повторення); включати в програму написані окремо фрагменти з налаштуванням їх тексту відповідно до заданих параметрів (макрокоманди). Об'єкти, що створюються за допомогою директив макромови, зазвичай називають макросами. Іноді, правда, термін макрос відносять тільки до одного конкретного виду макрозасобів, саме, до макрокоманди. Використання макросів спрощує складання початкового тексту програми і іноді робить цей текст наочнішим, хоча в окремих випадках, як, наприклад, у разі директив умовної трансляції, навпаки, може привести до істотного ускладнення початкового тексту.

Як і у всякій мові програмування, в мові макрозасобів є багато різного роду тонкощів, але в прикладному програмуванні часто використовуються лише базові можливості цієї мови. Тому ми обмежимося тут розглядом основних макрозасобів асемблера.

Блоки повторення

Блоки повторення примушують транслятор повторити заданий блок початкового тексту вказане число разів. Повторюваний блок може складатися з директив опису даних (і тоді він включається до складу сегменту даних) або з команд процесора (і тоді він описується в програмному сегменті). Наприклад, наступний фрагмент сегменту даних дозволяє утворити масив, що складається з код ASCII прописних російських букв:

sym='a' ;Начальное значення тимчасової змінної

symbols: ;Имя масиву для посилань на нього

rept 32 ;Повторять стільки раз

db sym ;Повторяемая директива

sym=sym+l ;Изменение змінній

endm ;Конец блоку повторення 

Як видно з приведеного фрагмента, блок повторення починається з директиви асемблера rept (від repetition, повторення), а закінчується директивою endm (end macro, кінець макросу). Реально в сегменті даних виділяється 32 байт, заповнених числами від 81h до 9fh, які передбачається розглядати, як послідовність російських букв. Того ж результату можна було досягти за допомогою наступної пропозиції:

symbols db "А", "Б", "В", "Г", і так далі до букви Я  


або простіше, хоча і менш наочно:

symbols db 128,129,130,131, і так далі до числа 159.  

Макрос повторення декілька скорочує час, потрібний для опису в тексті програми необхідного масиву, хоча, можливо, знижує наочність цього опису.
При підключенні до комп'ютера вимірювального або такого, що управляє устаткування іноді виникає необхідність уповільнити роботу процесора при зверненні до портів цього устаткування. Уповільнення здійснюється включенням в текст програми однієї або, якщо потрібний, декількох команд безумовного переходу на наступну пропозицію:

in Al,300h ;Первое звернення до устаткування

jmp а ;Задержка на якийсь час

а: jmp b ;выполнения

b: jmp з ;трех команд jmp

з: in Al,301h ;Следующее звернення до устаткування 

Для того, щоб не створювати багато непотрібних, по суті, влучний, такого роду пропозиції часто записують таким чином:

in AL, 300h ;Первое звернення до устаткування

jmp $+2 ;Задержка на якийсь час

jmp $+2 ;выполнения

jmp $+2 ;трех команд jmp

in Al,301h ;Следующее звернення до устаткування 

Тут використовується позначення лічильника поточної адреси S. При трансляції будь-якої команди в лічильнику поточної адреси міститься адреса цієї команди (зсув її першого байта). Команда короткого переходу займає 2 байт, поетом}' команда jmp $+2 здійснює перехід на команду, що йде услід.

Часто в подібних випадках обмежуються однією командою jmp, яка створює необхідну затримку в долі мікросекунди. У тих випадках, проте, коли пристрій сполучення з устаткуванням працює помітно повільніше за процесор, доводиться включати між командами звернення до портів 5-6 команд jmp. Такий фрагмент можна оформити у вигляді блоку повторення:

rept 6 jmp $+2 endm  

Це, мабуть, простіше, ніж писати 6 команд jmp.

Макроси повторення мають декілька різновидів, які ми тут не розглядатимемо.

Макрокоманди

Програми, написані на мові асемблера, часто містять ділянки тексту, що повторюються, з однаковою структурою. Таку ділянку тексту можна оформити у вигляді макроозначення, що характеризується довільним ім'ям і необов'язковим списком формальних аргументів. Після того, як таке визначення зроблене, поява в програмі рядка, що містить ім'я макроозначення і список фактичних аргументів (все це разом називають макрокомандою), приводить до генерації всього необхідного тексту, званого макророзширенням. Варіюючи фактичні аргументи, можна, зберігаючи незмінною структуру макророзширення, змінити окремі його елементи.
Макроозначення повинне починатися рядком з ім'ям макроозначення і директивою macro, в полі аргументів якої указується список формальних аргументів. Закінчується макроозначення директивою endm.

Хай в програмі потрібно неодноразово зберігати в стеку вміст трьох регістрів, але у кожному конкретному випадку номери регістрів і їх порядок відрізняються. Оформимо ці дії у вигляді макроозначення:

psh macroa,b,c 

push а

push b

push з

endm  

Поява в початковому тексті програми рядка

psh АХ, ВХ, СХ  

приведе до генерації наступного фрагмента тексту:

push AX push BX push CX  

Якщо ж в початковому тексті є рядок

psh DX, ES ВР  

то відповідне макророзширення матиме вигляд:

push DX

push ES

push BP  

Як фактичні аргументи можуть виступати будь-які позначення асемблера, допустимі для даної команди. Зокрема, макровиклик

psh mem, [BX], ES: [17h]  

приведе до наступного макророзширення:

push mem

push [BX]

push ES : [17h]  

Якщо якісь рядки макроозначення мають бути помічені (наприклад, з метою організації циклів), то позначення міток слід оголосити локальними за допомогою оператора local. В цьому випадку асемблер, генеруючи макророзширення, створюватиме власні позначення влучний, що не повторюються при повторних викликах однієї і тієї ж макрокоманди:

delay macro

local point

mov Cx,200

point: loop point

endm  

Макрос delay створює затримку фіксованої тривалості. Якщо в текст програми включити дві макрокоманди delay

 
.

delay

.

delay 

то їх макророзширення, підставлені в текст програми, виглядатимуть таким чином:

 .

mov CX, 20000

??0000: loop ??0000

.
mov CX, 20000

??0000: loop ??0000  

При повторних підстановках макроозначення транслятор замінює позначення влучні point на позначення, що розрізняються ??0000, ??0001 і так далі, забезпечуючи тим самим правильне виконання команд циклів і переходів.

Макрокоманди схожі з підпрограмами в тому відношенні, що в обох випадках ми описуємо деякий програмний фрагмент один раз, а звертаємося до нього багато разів, можливо, з передачею різних параметрів. Проте ці обчислювальні засоби розрізняються як за способом використання, так і по своїх можливостях.
Підпрограми дозволяють скоротити об'єм здійснимого файлу за рахунок опису ділянок програми, що повторюються, лише одного разу. При кожному виклику підпрограми командою call відбувається перехід на один і той же фрагмент програми, що містить підпрограму, а після виконання підпрограми - повернення назад в точку виклику. Текст підпрограми повністю визначається на етапі її написання, і зміни в ході виконання підпрограми можливі тільки за рахунок передачі нею тих або інших конкретних значень.

Механізм використання макросу інший. Кожна макрокоманда, що зустрілася транслятору в тексті програми, замінюється ним на повний текст макроозначення. Якщо макрокоманда містить параметри, то в процесі цієї заміни відбувається підстановка параметрів в текст макроозначення. Утворене таким чином макророзширення складає частина тексту програми, невідмітно від решти пропозицій програми і не потребує яких-небудь викликів. Через ці обставини макрокоманди виявляється декілька ефективніше за підпрограми за швидкістю виконання, особливо, якщо врахувати час, потрібний для підготовки параметрів перед викликом підпрограми (наприклад, проштовхування їх в стек). Навряд чи стоїть, проте, проводити таке порівняння. Підпрограми і макрокоманди мають різні сфери застосування.
Підпрограми служать для скорочення об'єму програми, підвищення її наочності і спрощення перебудови алгоритму виконання всього програмного комплексу шляхом зміни складу і порядку підпрограм, що викликаються. При цьому активне використання підпрограм може зменшити розмір всієї програми в десятки разів.
Сенс використання макрокоманд зовсім інший. Макрокоманди дозволяють спростити процес написання програми і, можна сказати, є засобом автоматизації програмування. При цьому мова макрокоманд надає великі можливості по зміні тексту макророзширення залежно від указуваних в макрокоманді параметрів. Проілюструємо ці можливості на простому прикладі макрокоманди виводу на екран символу. Такою макрокомандою можна користуватися в процесі відладки складних програм, щоб отримувати інформацію про вміст будь-яких елементів пам'яті. Приклад оформлений у вигляді закінченої програми, яка носить чисто демонстраційний характер.

;Приклад 2-1. Використання макрокоманди
sym macroc ;Имя і формальний аргумент
push AX ;Сохраним використовувані
push DX ;в макроозначенні регістри
mov АН, 02h ;Функция DOS виведення символу
mov Dl,c ;Заберем символ
int 21h ;Вызов DOS
pop DX ;Восстановим
pop AX ;регистры
endm ;Конец макроозначення
code segment
assume cs:code
main proc
sym 'w' ;Символ вказаний безпосередньо
sym ES : 0 ;Вывод першого байта PSP
sym Cs:msg ;Вывод першої букви з msg
lea Bx,msg-t-l ;Адрес другої букви з msg
sym [BX] ;Вывод другої букви
mov AX, 40h ;Настроим DS
mov Ds,ax ;на початок пам'яті
sym Ds:49h ;Вывод номери відеорежиму
mov Ax,4c00h ;Завершение програми
int 21h
main endp
msg db 'OK'
code ends  

Тексти макроозначень зазвичай розміщуються на самому початку програми, що дає можливість викликати макрокоманди з будь-яких точок програми. Змістовна частина макросу sym полягає у виклику функції 02h DOS, яка виводить на екран символ з регістра DL. Оскільки макрос використовує регістри АХ і DX, вони на початку макросу зберігаються в стеку, а перед його завершенням відновлюються. Як параметр макрокоманди можна використовувати будь-яке позначення асемблера, яке може інтерпретуватися, як адресу символу.
Сама програма умисне побудована декілька нестандартним чином. У ній є єдиний сегмент з текстом програми, в кінці якого поміщений рядок даних (слово 'ОК'). Таке розташування даних допустиме, проте для звернення до них необхідно використовувати заміну сегменту (як це зроблено в третьому рядку програми), оскільки програмний сегмент адресується через регістр CS. Сегмент стека в програмі відсутній, що не дуже добре, але для невеликих програм допустимо. Фактично під стек буде використаний самий низ сегменту команд, починаючи з адреси Fffeh. Оскільки наша програма має розмір, істотно менше 64к, таке розташування стека не приведе ні до яких неприємностей (при великому розмірі програми стек міг би почати затирати нижні рядки програми).
У програмі проілюстровано використання як фактичний аргумент макрокоманди різних конструкцій мови: безпосереднього позначення символу (що, напевно, позбавлено сенсу), прямого звернення до різних ділянок пам'яті по абсолютних адресах через регістри ES і DS, адресації з використанням символічного позначення поля даних. На рис. 2.18 приведено виведення програми.

Рис. 2.18. Виведення програми 2.1.

Як вже наголошувалося, при завантаженні програми в пам'ять в регістри DS і ES заноситься сегментна адреса префікса програми, тому адресація через ES дозволяє прочитати вміст PSP. Префікс містить, головним чином, дані, необхідні системі для обслуговування поточної програми, але, крім того, і декілька команд. Зокрема, префікс починається з команди CD 20h, яка вже давно не використовується, але в префіксі присутній ради забезпечення сумісності із старими версіями DOS. Перший байт цієї команди, якщо його розглядати, як код символу, відповідає елементу подвійної горизонтальної рамки (довгий знак рівності).

Занісши в регістр DS число 40h, ми набудували його на початок області даних BIOS, яка починається з абсолютної адреси 400h, займає 256 байт і містить різноманітні дані, використовувані BIOS в процесі обслуговування апаратури комп'ютера. Так, наприклад, за адресою 0 від початку цієї області зберігається базова адреса першого послідовного порту; за адресою 8 - адреса першого паралельного порту, а за адресою 491i - код поточного відеорежиму. При роботі в DOS відеоадаптер зазвичай настроюється на режим 3 (80x25 символів, 16 квітів). Будучи виведений на екран, код 3 утворює зображення червоного туза.

У тих випадках, коли макрокоманди складаються для конкретної програми, вони включаються в текст програми так, як це було зроблено в прикладі 2.1. Проте часто програміст оформляє у вигляді макрокоманд стандартні процедури загального призначення, наприклад, програмну затримку або вивід на екран рядка тексту. В цьому випадку тексти макроозначень доцільно помістити в макробібліотеку.

Макробібліотека є файлом з текстами макроозначень. Макроозначення записуються в цей файл точно в такому ж вигляді, як і в текст програми. Нижче приведений текст файлу макробібліотеки з довільним ім'ям MYMACRO.MAC, що містить дві макрокоманди.

;Макрокоманда endpr завершення програми
endpr macro ;Макрокоманда без параметрів
mov Ax,4c00h
int 2 In
endm ;Конец макрокоманди
;Макрокоманда delay програмної затримки
delay macro time, що настроюється ;Параметр - число кроків
locallabell,iabel2 ;Локальные влучні
push CX ;Сохраним зовнішній лічильник
mov Cx,time ;Получим фактичний параметр
Iabel2 : push CX ;Сохраним його в стеку
mov CX, 0 ;Пусть буде 64к кроків
labell: loop lanell ;Внутренний цикл
pop CX ;Извлечем зовнішній лічильник
loop Iabel2 ;Внешний цикл
pop CX ;Восстановим CX програми
endm ;Конец макрокоманди  

Для того, щоб транслятору були доступні макрокоманди з файлу MYMACRO.MAC, його слід на етапі трансляції під'єднати до початкового тексту програми директивою асемблера include:

include my macro, mac  

Всі макрокоманди, включені в цей файл, можна використовувати в будь-якому місці програми.

Директиви умовної трансляції

Директиви умовної трансляції (умовного асемблювання) дозволяють мати в початковому тексті програми різні варіанти окремих фрагментів програми, і шляхом завдання певних умов управляти процесом трансляції. Таким чином можна, наприклад, включати або виключати з тексту програми службові, налагоджувальні фрагменти або настроювати програму для виконання на заданому процесорі.

Хай, наприклад, в процесі відладки складної програми ми використовуємо підпрограму regs виводу на екран вмісту всіх регістрів процесора. Включаючи в різні місця програми виклик цієї підпрограми, ми маємо можливість контролювати хід її виконання, у тому числі і такі тонкі моменти, як, наприклад, розташування програми в пам'яті або інтенсивність використання стека. Для управління процесом трансляції передбачимо константу debug (відладка), ненульове значення якої вимагатиме налагоджувального варіанту трансляції, а нульове - робочого. Початок програми, а також ділянки з викликом налагоджувальної підпрограми виглядатимуть таким чином:

;debug=l ;Удалите символ ';'для налагоджувальній трансляції

;debug=0 ;Удалите ';' для робочої трансляції

... ;Текст програми

if debug ;Транслировать тільки якщо debug=l

call regs;Вызов налагоджувальної підпрограми

endif ;Конец блоку умовної трансляції

. ;Продовження програми

if debug ;Следующее включення налагоджувального блоку

call regs

endif

... ;Продовження програми


Зрозуміло, можна відладжувати програму в налагоджувальному варіанті, а потім видалити всі виклики допоміжної підпрограми regs уручну і отримати робочий варіант, проте на практиці зазвичай (або навіть завжди) виявляється, що після експлуатації програми протягом деякого часу в ній виявляються непомічені раніше помилки, що приводить до необхідності знову вставляти в неї налагоджувальні рядки. Часто цю процедуру доводиться повторювати багато разів. Використання в програмі директив умовної трансляції скорочують процедуру перетворення програми з налагоджувального варіанту в робочий або навпаки до операції стирання одного символу ";" на початку програми і усувають вірогідність випадкового внесення до програми нових помилок в процесі видалення або вставки налагоджувальних рядків.

Розглянемо ще один приклад застосування директив умовної трансляції. Як вже наголошувалося, сучасні процесори надають програмістові значну кількість додаткових команд, які можна використовувати в програмах реального режиму, але тільки, зрозуміло, якщо комп'ютер оснащений відповідним процесором. Неважко скласти універсальну програму', яку можна виконувати як на сучасних процесорах (у ефективнішому режимі), так і на старіших (з деякою втратою ефективності), якщо включити в неї директиви умовної трансляції цих додаткових команд. До таких команд, зокрема, відносяться команди збереження в стеку всіх регістрів загального призначення pusha і відновлення всіх регістрів рора. Приведемо приклад умовної трансляції цих команд, в якому використовується конструкція макромови if... else... endif:

i386=l
if i386
.386
endif
code segment use16
assume Cs:code
main proc
.
if i386
push ;Сохранение всіх регістрів однією командою
else
push AX
push CX
push DX
push BX
push BP
push SI
push DI
endif
. . . ;Использование регістрів після
;збереження їх значень
if 1386
рора ;Восстановление всіх регістрів однією командою
else
pop DI
pop SI
pop BP
pop BX
pop DX
pop CX
pop AX
endif  

Якщо на початку програми є оголошення i386=1, то, по-перше, в програму буде включена директива .386, що дозволяє використовувати в програмі додаткові команди, а по-друге, в подальших умовних блоках транслюватимуться ті їх ділянки, які містять команди процесора 80386. Якщо ж оголошення i386= 1 вилучити, то в умовних блоках транслюватимуться еквівалентні по суті, але менш ефективні послідовності команд МП 86.

загрузка...
Сторінки, близькі за змістом