Бiла схема Чорна схема

Програмування мовою Асемблера

Вступ

Програмувати роботу ЦЕОМ за допомогою вказівки абсолютних чи чисельних значень адрес і кодів команд надзвичайно складно. Тому було створено мови програмування, у яких для позначення адрес і команд використовуються мнемонічні позначення. Найбільш наближеним до архітектури процесора, під яким мається процесор сімейства Intel, є мова Асемблера. Мова Асемблера являє собою символьну форму запису машинної мови, у ньому замість адрес використовуються назви, константи записуються в звичній десятковій системі, команди у виді їхніх символьних назв.

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

До недоліків мови Асемблера можна віднести труднощі написання, налагодження і не мобільність програм.

Для персонального комп'ютера розроблено кілька мов Асемблера, можна назвати такі, як MASM декількох версій фірми Microsoft, TASM фірми Borland і ін. З огляду на те, що до складу відомого нам пакету Borland C++ v.3.1 входить "Турбоасемблер" TASM і "Турбодебагер" (турбовідладник) TD, ми будемо користуватися в мовою Асемблера стосовно TASM.

Створення програми на мові Асемблера включає наступні етапи:

Для розуміння мови Асемблера необхідно знати найпростішу регістрову модель процесора 8086 фірми Intel.

Оперативна пам'ять. Регістри

Оперативна пам'ять. Обсяг оперативної пам'яті ПК - 220 байтів (1 Мб). Байти нумеруються починаючи з 0, номер байта називається його адресою. Для посилань на байти пам'яті використовуються 20-розрядні адреси: від 00000 до FFFFF (у 16-ричной системі). Байт містить 8 розрядів (бітів), кожний з який може приймати значення 1 чи 0. Розряди нумеруються з права наліво від 0 до 7:

7 6 5 4 3 2 1 0

Байт - це найменша комірка пам'яті, що може мати адрусу. У ПК використовуються і більш великі комірки - слова і подвійні слова. Слово - це два сусідніх байти, розмір слова - 16 бітів (вони нумеруються з права наліво від 0 до 15). Адресою слова вважається адреса его першого байта (з меншою адресою); ця адреса може бути як парною та і непарною. Подвійне слово - це будь-які чотири сусідніх байти (два сусідніх слова), розмір такої комірки - 32 біта; адресою подвійного слова вважається адреса его першого байта.

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

Регістри

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

Усі регістри мають розмір слова (16 бітів), за кожним з них закріплена визначена назва. За призначеням та способом використання регістри можна розбити на наступні групи:

Тлумачення цих назв:

Регістри загального призначення можна використовувати у всіх арифметичних і логічних командах. У той же час кожен з них має визначену спеціалізацію, тобто для деяких команд необхідні тольки визначені регістри. Наприклад, команди множення і ділення вимагають, щоб один з операндів знаходився в регістрі AX чи в регістрах AX і DX (у залежності від розміру операнду), а команди керування циклом використовують регістр CX як лічильник циклу. Регістри BX і BP зазвичай використовуються як базові регістри, а SI і DI - як індексні. Регістр SP як правило вказує на вершину стеку, апаратно підтримуваного ПК.

Регістри AX, BX, CX і DX конструктивно влаштовані так, що можна отримувати доступ до їх старшої і молодшої половин; можна сказати, що кожний з цих регістрів складається з двох регістрів, розмір яких один байт і що позначаються через AH, AL, BH, BL, CH, CL, DH, DL відповідно (H - high, старша; L - low, молодша). Таким чином, з кожним з цих регістрів можна працювати як з єдиним цілим, так і з його частинами. Наприклад, можна записати слово в AX, а потім зчитати тільки частину слова з регістра AH чи замінити тільки частину в регістрі AL і т.д. Така структура регістрів дозволяє використовувати їх для роботи і з числами так і із символами.

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

Сегментні регістри CS, DS, SS і ES не можуть бути операндами ніяких команд, окрім стековых команд і команд пересилання. Ці регістри використовуються тільки для сегментування адрес.

Лічильник команд IP завжди містить адресу (зсув від початку програми) тієї команди, що повинна бути виконана наступною (початок програми зберігається в регістрі CS). Зміст регістра IP можна змінити тільки командами переходу.

Ознаки

Процесор має спеціальний регістр ознак. Ознака - це біт, що приймає значення "1", якщо деяку умову було виконано, і значення "0" у іншому випадку. У процесорі i8086 використовуються 9 ознак, кожній з них привласнена визначена назва. Усі вони зібрані в регістрі ознак, тобто кожена ознака - це один з розрядів регістра. Структура регістра ознак зображено на Рис. 1.

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

15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
x x x x OF DF IF TF SF ZF х AF x PF x CF

Рисунок 1 - Регістр ознак

До ознак умов відносять:

Представлення даних

Розглянемо представлення цілих чисел, рядків і адрес у процесорі i8086. Дійсні числа процесором i8086 не обробляються, операції над цими числами реалізуються програмним шляхом або виконуються співпроцесором. Шістнадцяткові числа записуються з літерою h на кінці, двійкові числа - з літерою b.

У загальному випадку під ціле число можна відвести будь-яку кількість байтів, однак система команд процесора i8086 підтримує числа розміром у байт, слово і подвійне слово.

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

Цілі числа без знаку можуть бути представлені у виді байта, слова чи подвійного слова в залежності від їхнього розміру. У виді байта представляються цілі від 0 до 255 (=28-1), у виді слова - цілі від 0 до 65535 (=216-1), у виді подвійного слова - цілі від 0 до 4 294 967 295 (=232-1). Числа записуються в двійковій системі числення, займаючи всі розряди комірки.

10010=64h=0110 0100b (байт)

15010=96h=1001 0110b (байт)

40410=194h=0000 0001 1001 0100b (слово)

61233310=957EDh=0000 0000 0000 1001 0101 0111 1110 1101b (подвійне слово)

Варто відразу зрозуміти особливості збереження слів і подвійних слів у пам'яті комп'ютера. Числа розміром у слово зберігаються в пам'яті в "переверненому" виді: молодші (зправа) 8 бітів числа розміщуються в першому байті слова, а старші 8 бітів - у другому байті (у 16-тковій системі: дві праві цифри - в першому байті, дві ліві цифри - в другому байті). Наприклад, число 30010=012Ch у виді слова зберігається в пам'яті як:

A A+1
2C 01

Але, якщо це слово завантажити в регістр AX, то його розташування в регістрі буде звичайним:

AH AL
AX 01 2C

Аналогічно зберігаються і числа у форматі подвійного слова. У першому его байті розміщаються молодші 8 бітів числа, у другому байті - попередні 8 бітів і т.д. Наприклад, число 12345678h зберігається в пам'яті як:

A A+1 A+2 A+3
78 56 34 12

У першому слові подвійного слова розміщаються молодші (праві) 16 бітів числа, а в другому слові - старші 16 бітів, причому в кожнім з цих двох слів у свою чергу використовується "перевернене" представлення.

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

Цілі числа зі знаком також формуються у виді байта, слова і подвійного слова. У виді байта числа записуються від -128 до 127, у вигляді слова - числа від -32768 до 32767, а у вигляді подвійного слова - числа від -2147483648 до 2147483647. При цьому числа записуються в додатковому коді: від’ємне число записується так само, як і беззнакове число (тобто в прямому коді), а від’ємне число -x записується беззнаковим числом 28-x (для байтів), 216-x (для слів) чи 232-x (для подвійних слів). Наприклад, додатковим кодом число (-10) є байт F6h (256-10), слово FFF6h чи подвійне слово FFFFFFF6h. У такий спосіб самий лівий біт інтерпретується як знаковий, якщо він дорівнює 1, то число вважається від’ємним, якщо 0 - позитивним. Якщо знакове ціле число формату байт містить одиницю тольки в знаковому розряді, то воно інтерпретується як -128. Аналогічно для слова це буде -32768, для подвійного слова -2147483648.

Знакові числа розміром у слово і подвійне слово записуються в пам'яті також у "переверненому" виді і знаковим бітом виявляється останій біт байту комірки. Наприклад:

-30010=FED4h

A A+1
D4 FE

-1234567810=FF439EB2

A A+1 A+2 A+3
B2 9E 43 FF

Символи і рядки. На символ відвлдиться один байт пам'яті, у який записується код символу - ціле від 0 до 255. У IBM-сумісних комп'ютерах використовується система кодування ASCII (American Standard Code for Information Interchange).

Деякі особливості цієї системи кодування:

Рядок - це послідовність символів, що розміщено в сусідніх байтах пам'яті, код першого символу рядка записується в першому байті, код другого символу - в другому байті і т.д. Адресою рядка вважається адреса його першого байта.

Представлення адрес. Адреса - це порядковий номер комірки пам'яті, тобто від’ємне ціле число, тому в загальному випадку адреси мають вигляд як і беззнакові числа. Часто під адресою розуміється 16-бітовий зсув (offset) - адреса комірки, що знаходиться на відстані “зсув” від початку сегмента (області) пам'яті, якому належить ця комірка. У цьому випадку під адресою розумуєтсья слово пам'яті і як число він записується в пам'яті в "переверненому" виді.

В іншому випадку під адресою розуміється 20-бітова абсолютна адреса деякої комірки пам'яті. Така адреса задається як пара сегмент:зсув, де сегмент (segment) - це перші 16 бітів початкової адреси сегмента пам'яті, якому належить комірка, а зсув - 16-бітова адреса цієї комірки, відносно початку даного сегмента пам'яті. Абсолютна адреса утворюється як сегмент*16+зсув. Такая пари записується в пам'яті у вигляді подвійного слова: у першому слові розміщається зсув, а в другому - сегмент (перевернений вид), причому кожне з цих слів у свою чергу представлено в переверненому виді. Наприклад, пари 1234h:5678h буде записано як:

Зсув Сегмент
78 56 34 12

Директиви визначення даних

Для резервування комірок пам'яті для констант і змінних та їх ініціалізації в мові Асемблера використовуються директиви визначення даних - з назвами DB (визначає дані розміром у байт), DW (визначає дані розміром у слово) і DD (визначає дані розміром у подвійне слово).

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

За допомогою директив DB, DW і DD можуть завдаватись одна чи декілька змінних, привласнюється їм назва. За цією директивою Асемблер формує машинне представлення значень змінних і записує їх у комірки пам'яті по черзі. Адреси цих комірок стають значеннями назв змінних, тобто усі входження назви в програму Асемблер буде заміщувати на відповідну цій назві адресу. Назви, зазначені в директивах DB, DW і DD, називаються назвами змінних. Приклади:

   A   DB   162      ;Резервування пам'яті для даних розміром 1 байт
                     ;занести в неї число 162 і дати їй назву A
   B   DW   -1       ;Виділити пам'ять розміром 2 байти, занести -1
   C   DD   -1       ;Подвійне слово

Дані в директиві DB можуть надаватися як числами, так і как символами: вказується або код символу (ціле від 0 до 255), або сам символ у лапках (одинарних чи подвійних); в останньому випадку Асемблер сам змінить символ на його код. Наприклад, наступні директиви еквівалентні (2A - код знаку * у ASCII):

   star      DB   02Ah
   star      DB   '*'
   star      DB   "*"

Якщо адресу необхідно задати як дані, то це робиться так:

   star       DB   '*'
   adr_star   DW   star

У цій директиві відведено слово пам'яті, якому надається назва adr_atar і в який запишеться адреса (зсув, ефективна адреса), що відповідає назві star. Якщо для аналогічної мети використовується директива DD:

   fadr_star      DD   star

Асемблер автоматично додасть до зсуву назви його сегмент і запише зсув у першу половину подвійного слова, а сегмент - у другу половину.

По кожній з директив DB, DW і DD можна описати змінну, тобто відвести комірку, не давши їй початкового значення. У цьому випадку в правій частині директиви вказується знак питання:

   Param_1   DW   ?   ;виділити слово, привласнити йому назву Param_1,
                      ;нічого в це слово не записувати

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

   betta      DB   200,-5,10h,?,'F'

Назва, зазначена в директиві, є назвою першого значення. Для посилань на решту в MASM використовуються вираз виду <назва>+<ціле>, наприклад, для доступу до байта з числом -5 використовується вираз betta+1, для доступу до байта зі значенням 10h - вираз betta+2 і т.д. Індексація починається з нуля. Якщо в директиві DB перераховані тілько символи, наприклад:

   str   DB   'a','b','c'

тоді цю директиву можна записати коротше, уклавши всі ці символи в одні лапки:

   str   DB   'abc'
чи
   str   DB   "abc"

Якщо в директиві описується несколько однакових констант (перемінних), то можна скористатися оператором повторення DUP. Наприклад

   mas   DB   5 dup (4)
;еквивалентно директиві
   mas   DB   4,4,4,4,4
;Інший приклад:
   arr   DW   3 dup (?),-50,2 dup (7)
;що еквивалентно директиві
   arr   DW   ?,?,?,-50,7,7

В Асемблере є директиви EQU і =, за допомогою яких можна визначити константи. Директива EQU привласнює назві значення, що визначається як результат цілочисельного виразу. Директива EQU аналогічна директиві #define у мові Си. Значення, що привласнено назві за допомогою директиви EQU, не можна в змінити.

Приклад:

   A   equ   10
   B   equ   21/3
   C   equ   "abcdef"

Директива "=" схожа на директиву EQU, але значення, привласнене назві повинне бути цілим числом і його можна перевизначати. Наприклад:

   alfa=20
   alfa=alfa+1

Для посилань на поточну комірку використовується позначення $, що є позначенням лічильника поточної адреси. Приклад:

   mas   DB   "assembler"
   mas_len=$-mas

У цьому прикладі значенням назви mas_len буде довжина рядка mas, тобто число 9.


Copyright by N.N.Khokhlov
© Ukraine ZNTU 2004
All rights reserved.