Директиви визначення даних
Оперативна пам'ять. Регістри
Оперативна пам'ять. Обсяг оперативної пам'яті ПК - 220 байтів (1 Мб). Байти нумеруються починаючи з 0, номер байта називається його адресою. Для посилань на байти пам'яті використовуються 20-розрядні адреси: від 00000 до FFFFF (у 16-ричной системі). Байт містить 8 розрядів (бітів), кожний з який може приймати значення 1 чи 0. Розряди нумеруються з права наліво від 0 до 7:
Байт - це найменша комірка пам'яті, що може мати адрусу. У ПК використовуються і більш великі комірки - слова і подвійні слова. Слово - це два сусідніх байти, розмір слова - 16 бітів (вони нумеруються з права наліво від 0 до 15). Адресою слова вважається адреса его першого байта (з меншою адресою); ця адреса може бути як парною та і непарною. Подвійне слово - це будь-які чотири сусідніх байти (два сусідніх слова), розмір такої комірки - 32 біта; адресою подвійного слова вважається адреса его першого байта.
Байти використовуються для збереження невеликих цілих чисел і символів, слова - для збереження цілих чисел і адрес, подвійні слова - для збереження "довгих" цілих чисел і адрес у виді сегмент:зсув.
Регістри
Крім комірок оперативної пам'яті для короткочасного збереження даних використовуються регістри - комірки, що входять до складу процесора. Доступ до регістрів здійснюється значно швидше, ніж до комірок пам'яті, тому використання регістрів помітно зменшує час виконання програм. Крім того, у деяких випадках без регістрів узагалі неможливо обійтися.
Усі регістри мають розмір слова (16 бітів), за кожним з них закріплена визначена назва. За призначеням та способом використання регістри можна розбити на наступні групи:
- регістри загального призначення (AX, BX, CX, DX, BP, SI, DI, SP);
- сегментні регістри (CS, DS, SS, ES);
- лічильник команд (IP);
- регістр ознак (Flags).
Тлумачення цих назв:
- A accumulator, акумулятор;
- B base, база;
- C counter, лічильник;
- D data, дані;
- BP base pointer, покажчик бази;
- SI source index, індекс джерела;
- DI destination index, індекс приймача;
- SP stack pointer, покажчик стеку;
- CS code segment, сегмент команд;
- DS data segment, сегмент даних;
- SS stack segment, сегмент стеку;
- ES extra segment, додатковий сегмент;
- IP instruction pointer, лічильник команд.
Регістри загального призначення можна використовувати у всіх арифметичних і логічних командах. У той же час кожен з них має визначену спеціалізацію, тобто для деяких команд необхідні тольки визначені регістри. Наприклад, команди множення і ділення вимагають, щоб один з операндів знаходився в регістрі 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.
Умовно ознаки розділяються на ознаки умов і ознаки станів. Ознаки умов автоматично змінюються при виконанні команд і фіксують ті чи інші властивості їх результату, наприклад, було чи ні переповнення або результат рівний нулю. Ознаки станів встановлюються програмою і визначають подальшу роботу процесора, наприклад, блокують переривання.
x | x | x | x | OF | DF | IF | TF | SF | ZF | х | AF | x | PF | x | CF |
Рисунок 1 - Регістр ознак
До ознак умов відносять:
- CF (carry flag) - ознака переносу. Приймає значення 1, якщо операція призвела до перенесення зі старшого біта результату. Старшим є 7-й, 15-й, 31-й біти в залежності від розмірності операнда. Це відбувається, наприклад, якщо при додаванні цілих чисел результат вийшов за межі розрядної сітки, чи якщо при відніманні чисел без знаку перше з них було менше за друге. У командах зрушення в CF заноситься біт, що вийшов за розрядну сітку;
- OF (overflow flag) - ознака переповнення. Встановлюється в 1, якщо в результаті операції було виконано перенос у старший, знаковий біт (7-й, 15-й, чи 31-й). Встановлюється в 1, якщо відбувається позика з цих розрядів. Ця ознака необхідна для роботи з числами зі знаком;
- ZF (zero flag) - ознака нуля. Дорівнює 1, якщо результат операції виявився рівним 0;
- SF (sign flag) - ознака знаку. Встановлюється в 1, якщо в операції над знаковими числами було отримано від’ємний результат;
- PF (parity flag) - ознака парності. Дорівнює 1, якщо результат чергової команди містить парну кількість двійкових одиниць. Враховується звичайно при операціях вводу-виводу;
- AF (auxiliary carry flag) - ознака додаткового переносу. Фіксує особливості виконання операцій над двоїчно-десятковими числами.
До ознак станів відносять:
- DF (direction flag) - ознака напрямку. Встановлює напрямок перегляду рядків у строкових командах: при DF=0 рядки проглядаються від початку до кінця, при DF=1 - у зворотному напрямку;
- IF (interrupt flag) - Ознака переривань. При IF=0 процесор перестає реагувати на переривання, що надходять до нього, при IF=1 блокування переривань знімається;
- TF (trap flag) - ознака трасування. При TF=1 після виконання кожної команди процесор робить переривання (з номером 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 |
Але, якщо це слово завантажити в регістр AX, то його розташування в регістрі буде звичайним:
AH | AL | |
AX | 2C |
Аналогічно зберігаються і числа у форматі подвійного слова. У першому его байті розміщаються молодші 8 бітів числа, у другому байті - попередні 8 бітів і т.д. Наприклад, число 12345678h зберігається в пам'яті як:
A | A+1 | A+2 | A+3 |
У першому слові подвійного слова розміщаються молодші (праві) 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 | FF |
Символи і рядки. На символ відвлдиться один байт пам'яті, у який записується код символу - ціле від 0 до 255. У IBM-сумісних комп'ютерах використовується система кодування ASCII (American Standard Code for Information Interchange).
Деякі особливості цієї системи кодування:
- код пробілу менше коду будь-якого буквеного символу, цифор й інших символів, що графічно відображаються;
- коди цифр упорядковані за величиною цифр і не містять пропусків - це від 30h, до 39h;
- коди великих і малих латинських і букв упорядковані відповідно до алфавіту і не містять пропусків;
Рядок - це послідовність символів, що розміщено в сусідніх байтах пам'яті, код першого символу рядка записується в першому байті, код другого символу - в другому байті і т.д. Адресою рядка вважається адреса його першого байта.
Представлення адрес. Адреса - це порядковий номер комірки пам'яті, тобто від’ємне ціле число, тому в загальному випадку адреси мають вигляд як і беззнакові числа. Часто під адресою розуміється 16-бітовий зсув (offset) - адреса комірки, що знаходиться на відстані “зсув” від початку сегмента (області) пам'яті, якому належить ця комірка. У цьому випадку під адресою розумуєтсья слово пам'яті і як число він записується в пам'яті в "переверненому" виді.
В іншому випадку під адресою розуміється 20-бітова абсолютна адреса деякої комірки пам'яті. Така адреса задається як пара сегмент:зсув, де сегмент (segment) - це перші 16 бітів початкової адреси сегмента пам'яті, якому належить комірка, а зсув - 16-бітова адреса цієї комірки, відносно початку даного сегмента пам'яті. Абсолютна адреса утворюється як сегмент*16+зсув. Такая пари записується в пам'яті у вигляді подвійного слова: у першому слові розміщається зсув, а в другому - сегмент (перевернений вид), причому кожне з цих слів у свою чергу представлено в переверненому виді. Наприклад, пари 1234h:5678h буде записано як:
Зсув | Сегмент | ||
Директиви визначення даних
Для резервування комірок пам'яті для констант і змінних та їх ініціалізації в мові Асемблера використовуються директиви визначення даних - з назвами 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.