МОВА МАКРОАССЕМБЛЕРА IBM PC (Довідковий посібник) p>
Укладач: В. Н. Пильщиків (МДУ, ВМК) (січень 1992 р.) p>
У посібнику розглядається мова макроассеблера для персональних ЕОМ типу IBM PC (мова MASM, версія 4.0). p>
Посібник складається з 4 розділів. У главі 1 розглянуто особливості персональних комп'ютерів типу IBM PC і наведені початкові відомості про мову MASM. У главі 2 описується система команд цих комп'ютерів. Глава 3 присвячена присвячена власне мови MASM. У розділі 4 наведено приклади фрагментів програм та повних програм на MASM для вирішення різних завдань. P>
У посібнику не розглядаються питання, пов'язані з обробкою двійковій-десяткових чисел і роботою арифметичного співпроцесора 8087 або 80287. p>
Під терміном "ПК" в посібнику розуміється персональний комп'ютер типу IBM PC c мікропроцесором 8088/8086, 80186 або 80286. p>
Розділ 1. ОСОБЛИВОСТІ ПК. ВСТУП У MASM. P>
1.1. ОПЕРАТИВНА ПАМ'ЯТЬ. РЕГІСТРИ. P>
1.1.1 Оперативна пам'ять p>
b>
Об'єм оперативної пам'яті ПК - 2 ^ 20 байт (1 Мб). Байти нумеруються починаючи з 0, номер байта називається його адресою. Для посилань на байти пам'яті використовуються 20-розрядні адреси: від 00000 до FFFFF (у 16-річної системі). P>
Байт містить 8 розрядів (бітів), кожен з яких може приймати значення 1 або 0. Розряди нумеруються справа наліво від 0 до 7: p>
-----------------< br>
| | | | | | | | |
-----------------< br>
7 6 5 4 3 2 1 0
Байт - це найменша адресуемая комірка пам'яті. У ПК використовуються і більш великі осередки - слова і подвійні слова. Слово - це два сусідніх байти, розмір слова - 16 бітів (вони нумеруються справа наліво від 0 до 15). Адресою слова вважається адреса її першого байта (з меншим адресою); ця адреса може бути парним і непарним. Подвійне слово - це будь-які чотири сусідніх байти (два сусідніх слова), розмір такої комірки - 32 біта; адресою подвійного слова вважається адреса її першого байта. p>
Байти використовуються для зберігання невеликих цілих чисел і символів, слова - для зберігання цілих чисел і адрес, подвійні слова - для зберігання "довгих" цілих чисел і т.зв. адресних пар (сегмент: зсув). p>
1.1.2 Регістри p>
b>
Крім комірок оперативної пам'яті для зберігання даних (щоправда, короткочасного) можна використовувати і регістри - комірки, що входять до складу процесора і доступні з машинної програми. Доступ до регістрів здійснюється значно швидше, ніж до комірок пам'яті, тому використання регістрів помітно зменшує час виконання програм. P>
Всі регістри мають розмір слова (16 бітів), за кожним з них закріплено певне ім'я (AX, SP і т.п.). За призначенням і способом використання регістри можна розбити на наступні групи: p>
регістри загального призначення (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 - p>
code segment, сегмент команд; DS - data segment, сегмент даних; SS stack segment, сегмент стека; ES - extra segment, додатковий сегмент; IP - instruction pointer, лічильник команд.) p>
Регістри загального призначення можна використовувати у всіх арифметичних і логічних командах. У той же час кожен з них має певну спеціалізацію (деякі команди "працюють" тільки з певними регістрами). Наприклад, команди множення і ділення вимагають, щоб один з операндів знаходився в регістрі AX або в регістрах AX і DX (залежно від розміру операнда), а команди управління циклом використовують регістр CX в якості лічильника циклу. Регістри BX і BP дуже часто використовуються як базові регістри, а SI і DI - як індексні. Регістр SP зазвичай вказує на вершину стека, апаратно підтримуваного в ПК. P>
Регістри AX, BX, CX і DX конструктивно влаштовані так, що можливий незалежний доступ до їх старшої та молодшої половин; можна сказати, що кожен з цих регістрів складається з двох байтових регістрів, що позначаються AH, AL, BH і т.д . (H - high, старший; L - low, молодший): p>
----------- ----------- ----------- -----------< Br>
AX | AH | AL | BX | BH | BL | CX | CH | CL | DX | DH | DL |
----------- ----------- ----------- -----------< Br>
15 8 7 0
Таким чином, з кожним із цих регістрів можна працювати як з єдиним цілим, а можна працювати і з його "половинками". Наприклад, можна записати слово в AX, а потім вважати тільки частину слова з регістру AH або замінити тільки частину в регістрі AL і т.д. Такий пристрій регістрів дозволяє використовувати їх для роботи і з числами, і з символами. P>
Всі інші регістри не діляться на "половинки", тому вважати чи записати їх вміст (16 бітів) можна тільки цілком. p>
Сегментні регістри CS, DS, SS і ES не можуть бути операндами ніяких команд, крім команд пересилання і стекові команд. Ці регістри використовуються тільки для сегментації адрес (див. 1.4). P>
Лічильник команд IP завжди містить адресу (зсув від початку програми) тієї команди, яка повинна бути виконана наступної (початок програми зберігається в регістрі CS). Вміст регістра IP можна змінити тільки командами переходу. P>
1.1.3 Прапори p>
b>
І, нарешті, в ПК є особливий регістр прапорів. Прапор - це біт, що приймає значення 1 ( "прапор встановлений"), якщо виконано деякий умова, і значення 0 ( "прапор скинутий") в іншому випадку. У ПК ис- p>
користується 9 прапорів, кожному з них присвоєно певне ім'я (ZF, CF і т.д.). Всі вони зібрані в регістрі прапорів (кожен прапор - це один з розрядів регістра, частина його розрядів не використовується): p>
-------------------------------------------------< br>
Flags | x | x | x | x | OF | DF | IF | TF | SF | ZF | x | AF | x | PF | x | CF |
-------------------------------------------------< br>
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
Деякі прапори прийнято називати прапорами умов; вони автоматично міняються при виконанні команд і фіксують ті чи інші властивості їх результату (наприклад, чи він дорівнює нулю). Інші прапори називаються прапорами станів; вони міняються з програми і впливають на подальшу поведінку процесора (наприклад, блокують переривання). P>
Прапори умов: p>
CF (carry flag) - прапорець перенесення. Приймає значення 1, якщо при складанні цілих чисел з'явилася одиниця переносу, не "влазити" в розрядну сітку, або якщо при віднімання чисел без знаку першим з них було менше другий. У командах зрушення в CF заноситься біт, що вийшов за розрядну сітку. CF фіксує також особливості команди множення. P>
OF (overflow flag) - прапорець переповнення. Встановлюється в 1, якщо при складання або віднімання цілих чисел зі знаком вийшов результат, по модулю перевершує допустиму величину (сталося переповнення мантиси і вона "залізла" в знаковий розряд). P>
ZF (zero flag) - прапор нуля. Встановлюється в 1, якщо результат команди виявився рівним 0. P>
SF (sign flag) - прапор знаку. Встановлюється в 1, якщо в операції над знаковими числами вийшов негативний результат. P>
PF (parity flag) - прапорець парності. Дорівнює 1, якщо результат чергової команди містить парна кількість двійкових одиниць. Враховується звичайно тільки при операціях вводу-виводу. P>
AF (auxiliary carry flag) - прапорець додаткового переносу. Фіксує особливості виконання операцій над двійковій-десятковими числами. p>
Прапори станів: p>
DF (direction flag) - прапорець напрямку. Встановлює напрямок перегляду рядків у строкових командах: при DF = 0 рядки проглядаються "вперед" (від початку до кінця), при DF = 1 - у зворотному напрямку. P>
IF (interrupt flag) - прапорець переривань. При IF = 0 процесор перестає реагувати на що надходять до нього переривання, при IF = 1 блокування переривань знімається. P>
TF (trap flag) - прапорець трасування. При TF = 1 після виконання кожної команди процесор робить переривання (з номером 1), чим можна скористатися при налагодженні програми для її трасування. P>
1.2. ПРЕДСТАВЛЕННЯ ДАНИХ. Арифметичні операції p>
b>
Тут розглядається машинне подання цілих чисел, рядків і адрес. Представлення двійковій-десяткових чисел, що використовуються досить рідко, не розглядається. Що стосується дійсних чисел, то в ПК немає команд речової арифметики (операції над цими числами реалізуються програмним шляхом або виконуються співпроцесором) і тому немає стандартного подання дійсних чисел. Крім того, розглядаються деякі особливості виконання арифметичних операцій. p>
шістнадцяткові числа записуються з буквою h на кінці, двійкові p>
числа - з буквою b (так прийнято в MASM). p>
1.2.1 Представлення цілих чисел. p>
b>
У загальному випадку під ціле число можна відвести будь-яке число байтів, проте система команд ПК підтримує тільки числа розміром в байт і слово і частково підтримує числа розміром в подвійне слово. Саме ці формати і будуть розглянуті. P>
У ПК робиться відмінність між цілими числами без знаку (невід'ємними) та зі знаком. Це пояснюється тим, що в осередках одного і того ж розміру можна уявити більший діапазон беззнакових чисел, ніж невід'ємних знакових чисел, і якщо заздалегідь відомо, що деяка числова величина є невід'ємне, то вигідніше розглядати її як беззнакові, ніж як знакову. P >
Цілі числа без знака. p>
Ці числа можуть бути представлені у вигляді байта, слова чи подвійного слова - в залежності від їх розміру. У вигляді байти представляються цілі від 0 до 255 (= 2 ^ 8-1), у вигляді слова - цілі від 0 до 65535 (= 2 ^ 16-1), у вигляді подвійного слова - цілі від 0 до 4 294 967 295 ( = 2 ^ 32-1). Числа записуються в двійковій системі числення, займаючи всі розряди осередку. Наприклад, число 130 записується у вигляді байтів 10000010b (82h). P>
Числа розміром в слово зберігаються в пам'яті в "перевернутому" вигляді: молодшого (праві) 8 бітів числа розміщуються в першому байті слова, а старші 8 бітів - у другому байті (у 16-річної системі: дві праві цифри - в першу байті, дві ліві цифри - у другому байті). Наприклад, число 130 (= 0082h) у вигляді слова зберігається в пам'яті так:
-----------< br>
| 82 | 00 |
-----------< br>
(Зазначимо, однак, що в регістрах числа зберігаються в нормальному вигляді:
-----------< br>
AX | 00 | 82 |
-----------< br>
AH AL)
"перевернути" подання використовується і при зберіганні в пам'яті цілих чисел розміром в подвійне слово: у першій його байті розміщуються молодші 8 бітів числа, у другому байті - попередні 8 бітів і т.д. Наприклад, число 12345678h зберігається в пам'яті так: p>
---------------------< br>
| 78 | 56 | 34 | 12 |
---------------------< br>
Іншими словами, в першому слові подвійного слова розміщуються молодші (праві) 16 бітів числа, а в другому слові - старші 16 бітів, причому в кожному з цих двох слів в свою чергу використовується "перевернуте" подання. p>
Таке незвичайне подання чисел пояснюється тим, що в перших моделях ПК за раз можна було вважати з пам'яті тільки один байт і що всі арифметичні операції над багатозначними числами починаються з дій над молодшими цифрами, тому з пам'яті в першу чергу треба зчитувати молодші цифри , якщо відразу не можна вважати всі цифри. З огляду на це, в першу ПК і стали розміщувати молодші цифри числа перед старшими ціфраммі, а заради наступності таке подання чисел зберегли в наступних моделях ПК. P>
Звичайно, "перевернуте" подання незручно для людей, однак при використанні мови асемблера цю незручність не відчувається: в MASM всі числа записуються в нормальному, неперевернутом вигляді (див. нижче). p>
Цілі числа зі знаком. p>
Ці числа також подаються у вигляді байти, слова та подвійного слова. У вигляді байти записуються числа від -128 до 127, у вигляді слова числа від -32768 до 32767, а у вигляді подвійного слова - числа від -2147483648 до 2147483647. При цьому числа записуються в додатковому коді: невід'ємне число записується так само, як і беззнакові число (тобто в прямому коді), а негативне число-x (x> 0) представляється беззнакові числом 2 ^ 8-x (для байтів) , 2 ^ 16-x (для слів) або 2 ^ 32-x (для подвійних слів). Наприклад, додатковим кодом числа -6 є байт FAh (= 256-6), слово FFFAh або подвійне слово FFFFFFFAh. При цьому байт 10000000b (= 80h) трактується як -128, а не як +128 (слово 8000h розуміється як -32678), тому лівий біт додаткового коду завжди грає роль знакового: для невід'ємних чисел він дорівнює 0, для негативних - 1.
Знакові числа розміром в слово і подвійне слово записуються в пам'яті в "перевернутому" вигляді (при цьому знаковий біт виявляється в останньому байті комірки). Але в MASM ці числа, як і беззнакові, записуються в нормальній формі. P>
Іноді число-байт необхідно розширити до слова, тобто потрібно отримати таке ж за величиною число, але розміром в слово. Існує два способи такого розширення - без знаку і зі знаком. У будь-якому випадку вихідне число-байт потрапляє в другій (до "перевертання") байт слова, а ось перший байт заповнюється по-різному: при розширенні без знака в нього записуються нульові біти (12h -> 0012h), а при розширенні зі знаком в перший байт записуються нулі, якщо число-байт було невід'ємним, і записується вісім двійкових одиниць у противному випадку (81h -> FF81h). Іншими словами, при розширенні зі знаком в першому байті слова копіюється знаковий розряд числа-байти. P>
Аналогічно відбувається розширення числа-слова до подвійного слова. p>
1.2.2 Особливості виконання арифметичних опреацій p>
b>
У ПК є команди додавання і віднімання цілих чисел розміром в слово і байт. Спеціальних команд для додавання і віднімання подвійних слів немає, ці операції реалізуються через команди додавання і віднімання слів. p>
Додавання і віднімання беззнаковаих чисел проводиться за модулем 2 ^ 8 p>
для байтів і 2 ^ 16 для слів. Це означає, що якщо в результаті складання з'явилася одиниця переносу, не вміщаються в розрядну сітку, то вона відкидається. Наприклад, при складанні байтів 128 і 130 виходить число 258 = 100000010b, тому ліва двійкова одиниця відкидається і залишається число 2 = 10b, яке і оголошується результатом складання. Помилка тут не фіксується, але в прапор переносу CF записується 1 (якщо переносу не було, в CF заноситься 0). "Зловити" таке спотворення суми можна тільки подальшим аналізом прапора CF. P>
Спотворення результату відбувається і при віднімання від меншого числа більшого. І тут не фіксується помилка, однак перші числа дається "позика одиниці" (у разі байтів це число збільшується на 256, для p>
слів - на 2 ^ 16), після чого і проводиться віднімання. Наприклад, віднімання байтів 2 і 3 зводиться до віднімання чисел 256 +2 = 258 і 3, в результаті чого виходить неправильна різниця 255 (а не -1). Для того щоб можна було виявити таку ситуацію, в прапор переносу CF заноситься 1 (якщо позики не було, в CF записується 0). P>
Додавання і віднімання знакових цілих чисел здійснюється за тими ж алгоритмами, що і для беззнакових чисел (у цьому одна з переваг додаткового коду): знакові числа розглядаються як відповідні беззнакові числа, проізодітся операція над цими беззнакові числами і отриманий результат інтерпретується як знакову число. Наприклад, складання байтових чисел 1 і -2 відбувається так: беруться їхні додаткові коди 1 і (256-2) = 254, обчислюється сума цих величин 1 +254 = 255 і вона трактується як знакове число -1 (255 = 256-1) . Якщо при такому складання виникла одиниця переносу, то вона, як завжди, відкидається, а прапор CF отримує значення 1. Проте в даному випадку це відсікання не представляє інтерес - результат операції буде правильним, наприклад: 3 + (-2) => 3 +254 (mod 256) = 257 (mod 256) = 1. Зате тут можлива інша неприємність: модуль суми (її мантиса) може перевершити допустиму межу і "залізти" в знаковий розряд, зіпсували його. Наприклад, при складанні байтових чисел 127 і 2 виходить величина 129 = = 100001001b, що представляє додатковий код числа -127 (= 256-129). P>
Хоча результат тут вийшов і неправильним, процесор не фіксує помилку, але зате заносить 1 в прапор переповнення OF (якщо "переповнення мантиси" не було, в OF записується 0). Аналізуючи потім цей прапор, можна "зловити" таку помилку. P>
Таким чином, додавання (віднімання) знакових і беззнакових чисел здійснюється за одним і тим же алгоритмом. При цьому ПК не "знає", які числа (зі знаком чи без) він складає; в будь-якому випадку він складає їх як беззнакові числа і в будь-якому випадку формує прапори CF і OF. А ось як інтерпретувати складові і суму, на якій з цих прапорів звертати увагу - це особиста справа автора програми. P>
Що стосується множення і ділення знакових і беззнакових чисел, то вони виконуються за різними алгоритмами, різними машинними командами. Однак і у цих операцій є ряд особливостей. При збільшенні байтів (слів) перший співмножники зобов'язаний знаходитися в регістрі AL (AX), результатом ж множення є слово (подвійне слово), кіт?? рої заноситься в регістр AX (регістри DX і AX). Тим самим при збільшенні зберігаються всі цифри твори. При розподілі байтів (слів) перший операнд (ділене) повинен бути словом (подвійним словом) і зобов'язаний знаходитися в регістрі AX (регістрах DX і AX). Результатом поділу є дві величини розміром в байт (слово) - неповна приватне (div) і залишок від ділення (mod); неповне приватне записується в регістр AL (AX), а залишок - в регістр AH (DX). P>
1.2.3 Представлення символів і рядків p>
b>
На символ відводиться один байт пам'яті, в який записується код символу - ціле від 0 до 255. У ПК використовується система кодування ASCII (American Standard Code for Information Interchange). Вона, природно, не містить кодів російських букв, тому в нашій країні застосовується деякий варіант цієї системи з російськими літерами (зазвичай це альтернативна кодування ГОСТу). P>
Деякі особливості цих систем кодування: p>
- код пробілу менше коду будь-якої букви, цифри і взагалі будь-якого графічно представимо символу; p>
- коди цифр впорядковані за величиною цифр і не містять пропусків, тобто з нерівності код ('0 ') <= код (c) <= код ('9') випливає, що c - цифра; p>
- коди великих латинських букв впорядковані згідно з алфавітом і не містять пропусків; аналогічно з малими латинськими літерами; p>
- (в альтернативному кодуванні ГОСТу) коди російських букв (як великих, так і малих) впорядковані згідно з алфавітом, але між ними є коди інших символів. p>
Рядок (послідовність символів) розміщується в сусідніх байтах пам'яті (у неперевернутом вигляді): код першого символу рядка записується в першому байті, код другу символу - у другому байті і т.п. Адресою рядка вважається адреса її першого байта. P>
У ПК рядком вважається також і послідовність слів (зазвичай це p>
послідовність цілих чисел). Елементи таких рядків розташовуються в послідовних елементах пам'яті, але кожен елемент представлений в "перевернутому" вигляді. P>
1.2.4 Представлення адрес p>
b>
Адреса - це порядковий номер елементу пам'яті, тобто невід'ємне ціле число, тому в загальному випадку адреси подаються так само, як і беззнакові числа. Однак у ПК є ряд особливостей у поданні адрес. P>
Справа в тому, що в ПК терміном "адреса" позначають різні речі. Часто під адресою розуміється 16-бітове зміщення (offset) - адреса клітинки, відраховувати від початку сегмента (області) пам'яті, якому належить ця клітинка. У цьому випадку під адреса відводиться слово пам'яті, причому адреса записується в "перевернутому" вигляді (як і числа-слова взагалі). P>
В іншому випадку під "адресою" розуміється 20-бітовий абсолютний адреса деякої комірки пам'яті. Через низку причин в ПК така адреса задається не як 20-бітове число, а як пара "сегмент: зсув", де "сегмент" (segment) - це перші 16 бітів початкової адреси сегменту пам'яті, якому належить осередок, а "зсув" - 16-бітовий адреса цього осередку, відраховувати від початку даного сегменту пам'яті (величина 16 * сегмент + зсув даетабсолютний адреса комірки). Така пара записується у вигляді подвійного слова, причому (як і для чисел) в "перевернутому" вигляді: у першому слові розміщується зміщення, а в другому - сегмент, причому кожне з цих слів у свою чергу представлено в "перевернутому" вигляді. Наприклад, пара 1234h: 5678h буде записана так:
---------------------< br>
| 78 | 56 | 34 | 12 |
---------------------< br>
зсув сегмент
1.2.5 Директиви визначення даних p>
b>
Для того щоб у програмі на MASM зарезервувати комірки пам'яті під константи і змінні, необхідно скористатися вказівками визначення даних - з назвами DB (описує дані розміром в байт), DW (розміром в слово) і DD (розміром в подвійне слово). (Директиви, або команди асемблеру, - це пропозиції програми, якими її автор повідомляє якусь інформацію асемблеру або просить щось зробити додатково, крім перекладу символьних команд на машинний мова.) P>
У найпростішому випадку в директиві DB, DW або DD описується одна константа, якій дається ім'я для наступних посилань на неї. По цій директиві асемблер формує машинне подання константи (зокрема, якщо треба, "перевертає" її) і записує в чергову комірку пам'яті. Адреса цієї комірки стає значенням імені: всі входження імені в програму асемблер буде замінювати на цю адресу. Імена, зазначені в директивах DB, DW і DD, називаються іменами змінних (на відміну від міток - імен команд). P>
У MASM числа записуються в нормальному (неперевернутом) вигляді в cистемах числення з основою 10, 16, 8 або 2. Десяткові числа записуються як звичайно, за шістнадцяткові числом ставиться буква h (якщо число починається з "цифри" A, B, ..., F, то спочатку обов'язковий 0), за вісімковий числом - буква q або o, за двійковим числом - буква b. p>
Приклади: p>
A DB 162; описати константу-байт 162 і дати їй ім'я A
B DB 0A2h; така ж константа, але з ім'ям B
З DW -1; константа-слово -1 з ім'ям З
D DW 0FFFFh; така ж константа-слово, але з ім'ям D
E DD -1; -1 як подвійне слово
Константи-символи описуються в директиві DB двояко: або вказується код символу (ціле від 0 до 255), або сам символ в лапках (одинарних чи подвійних); в останньому випадку асемблер сам замінить символ на його код. Наприклад, такі директиви еквівалентні (2A - код зірочки в ASCII):
CH DB 02Ah
CH DB '*' CH DB "*"
Константи-адреси, як правило, задаються іменами. Так, за директивою
ADR DW CH
буде відведено слово пам'яті, якому дається ім'я ADR і в який запишеться адреса (зміщення), який відповідає імені CH. Якщо таке ж ім'я описати в директиві DD, то асемблер автоматично додасть до зміщення імені його сегмент і запише зміщення у першу половину подвійного слова, а сегмент - в другу половину. P>
З будь-якої з директив DB, DW і DD можна описати змінну, тобто відвести клітинку, не давши їй початкового значення. У цьому випадку в правій частині директиви вказується знак питання:
F DW? ; відвести слово і дати йому ім'я F, нічого в цей байт не записувати
В одній директиві можна описати відразу кілька констант і/або змінних одного і того ж розміру, для чого їх треба перерахувати через кому. Вони розміщуються в сусідніх комірках пам'яті. Приклад: p>
G DB 200, -5, 10h,?, 'F' p>
Ім'я, вказане в директиві, вважають саме перше з констант. Для посилань на інші в MASM використовуються вирази виду <ім'я> + <ціле>; наприклад, для доступу до байту з числом -5 треба вказати вираз G 1, для доступу до байту з 10h - вираз G 2 і т. д. p>
Якщо у директиві DB перераховані тільки символи, наприклад: p>
S DB 'a','+',' b' p>
тоді цю директиву можна записати коротше, уклавши всі ці символи в одні лапки: p>
S DB 'a + b' p>
І, нарешті, якщо у директиві описується кілька однакових констант (змінних), то можна скористатися конструкцією повторення p>
k DUP (a, b ,..., c) p>
що еквівалентна повтореної k раз послідовності a, b ,..., c. Наприклад, директиви p>
V1 DB 0,0,0,0,0
V2 DW ?,?,?,?,?,?,?,?,?,' a ', 1,2,1,2,1,2,1,2
можна записати більш коротко таким чином:
V1 DB 5 DUP (0)
V2 DW 9 DUP (?), 'A', 4 DUP (1,2)
1.3. ПОДАННЯ КОМАНД. Модифікація адреси.
P>
1.3.1 Структура команд. Виконавчі адреси
b> Машинні команди ПК займають від 1 до 6 байтів.
Код операції (КОП) займає один або два перших байтів команди. У ПК настільки багато різних операцій, що для них не вистачає 256 різних КОПів, які можна представити в одному байті. Тому деякі операції об'єднуються в групи і їм дається один і той же КОП, у другому ж байті цей КОП уточнюється. Крім того, у другому байті вказуються типи і спосіб адресації операндів. Решта байти команди вказують на операнди.
Команди можуть мати від 0 до 3 операндів, у більшості команд один або два операнда. Розмір операндів - байт або слово (рідко подвійне слово). Операнд має бути зазначено у самій команді (це т.зв. безпосередній операнд), або може перебувати в одному з регістрів ПК і тоді в команді вказується цей регістр, або може перебувати в комірці пам'яті і тоді в команді той чи інший спосіб вказується адреса цієї осередки. Деякі команди вимагають, щоб операнд знаходився у фіксованому місці (наприклад, в регістрі AX), тоді операнд явно не вказується в команді. Результат виконання команди поміщається в регістр або комірку пам'яті, з якого (яку), як правило, береться перший операнд. Наприклад, більшість команд з двома операндами реалізують дію
op1: = op1 _ op2
де op1 - регістр або комірка, а op2 - безпосередній операнд, регістр або комірка.
Адреса операнда дозволено модифіковані з одного або двох регістрів. У першому випадку як регістра-модифікатора дозволено використовувати регістр BX, BP, SI або DI (і ніякий інший). У другому випадку один з модифікаторів зобов'язаний бути регістром BX або BP, а інший -
регістром або SI DI; одночасна модифікація з BX і BP або SI і DI неприпустима. Регістри BX і BP зазвичай використовуються для зберігання бази (початкової адреси) деякої ділянки пам'яті (скажімо, масиву) і тому називаються базовими регістрами, а регістри SI і DI часто містять індекси елементів масиву і тому називаються індексними регістрами. Однак такий розподіл ролей необов'язково, і, наприклад, в SI може знаходитися база масиву, а в BX - індекс елемента масиву.
У MASM адреса в командах записуються у вигляді однієї з наступних конструкції:
A, A [M] або A [M1] [M2],
де A - адреса, M - регістр BX, BP, SI або DI, M1 - регістр BX або BP, а M2 - регістр SI або DI. Під второрм і третьому варіанті A може бути відсутнім, в цьому випадку вважається, що A = 0.
При виконанні команди процесор насамперед обчислює т.зв. виконавчий (ефективний) адреса - як суму адреси, заданого в команді, і поточних значень зазначених регістрів-модифікаторів, причому всі ці величини розглядаються як невід'ємні і підсумовування
ведеться за модулем 2 ^ 16 ([r] означає вміст регістра r):
A: Aісп = A
A [M]: Aісп = A + [M] (mod 2 ^ 16)
A [M1] [M2]: Aісп = A + [M1] + [M2] (mod 2 ^ 16)
Отриманий таким чином 16-розрядний адреса визначається т.зв. зсув - адреса, відраховувати від початку деякого сегмента (області) пам'яті. Перед зверненням до пам'яті процесор ще додає до зміщення початковий адресу цього сегменту (він зберігається в деякому сегментному регістрі), в результаті чого виходить остаточний 20-розрядний адреса, за якою і відбувається реальне звернення до пам'яті (див. 1.4).
1.3.2 Формати команд
b> У ПК формати машинних команд досить різноманітні. Для прикладу наведемо лише основні формати команд з двома операндами.
1) Формат "регістр-регістр" (2байта):
------------- ----------------< Br>
| КОП | d | w | | 11 | reg1 | reg2 |
------------- ----------------< Br>
7 2 1 0 7 6 5 3 2 0
Команди цього формату описують зазвичай дію reg1: = reg1_reg2 або
reg2: = reg2_reg1. Поле КОП першого байта вказує на операцію (_), яку треба виконати. Біт w визначає розмір операндів, а біт d вказує, в якій із регістрів записується результат:
w = 1 - слова d = 1 - reg1: = reg1_reg2
= 0 - байти = 0 - reg2: = reg2_reg1
У другому байті два лівих біта фіксовані (для даного формату), а трехбітовие поля reg1 і reg2 вказують на регістри, що беруть участь в операції, згідно наступної таблиці:
reg w = 1 w = 0 reg w = 1 w = 0
----------------- ----------------< Br>
000 AX AL 100 SP AH
001 CX CL 101 BP CH
010 DX DL 110 SI DH
011 BX BL 111 DI BH
2) Формат "регістр-пам'ять" (2-4 байти):
------------- ------------- -------------------< Br>
| КОП | d | w | | mod | reg | mem | | адреса (0-2 байти) |
------------- ------------- -------------------< Br>
Ці команди описують операції reg: = reg_mem або mem: = mem_reg. Біт w першого байта визначає розмір операндів (див. вище), а біт d вказує, куди записується результат: в регістр (d = 1) або в комірку пам'яті (d = 0). Трехбітовое поле reg другому байти вказує операнд-регістр (див. вище), двухбітовое поле mod визначає, скільки байтів в команді займає операнд-адреса (00 - 0 байтів, 01 - 1 байт, 10 - 2 байти), а трехбітовое поле mem вказує спосіб модифікації цієї адреси. У наступній таблиці вказані правила обчислення виконавчого адреса в залежності від значень полів mod і mem (a8 - адреса розміром в байт, a16
- Адреса розміром в слово):
mem mod | 00 01 10
-------------------------------------------------- ----< br>
000 | [BX] + [SI] [BX] + [SI] + a8 [BX] + [SI] + a16
001 | [BX] + [DI] [BX] + [DI] + a8 [BX] + [DI] + a16
010 | [BP] + [SI] [BP] + [SI] + a8 [BP] + [SI] + a16
011 | [BP] + [DI] [BP] + [DI] + a8 [BP] + [DI] + a16
100 | [SI] [SI] + a8 [SI] + a16
101 | [DI] [DI] + a8 [DI] + a16
110 | a16 [BP] + a8 [BP] + a16
111 | [BX] [BX] + a8 [BX] + a16
Зауваження. Якщо в команді не задано адресу, то він вважається нульовим. Якщо адреса задано у вигляді байт (a8), то він автоматично розширюється зі знаком до слова (a16). Випадок mod = 00 і mem = 110 вказує на відсутність регістрів-модифікаторів, при цьому адреса должет мати розмір слова (адресний вираз [BP] асемблер транслює в mod = 01 і mem = 110 при a8 = 0). Випадок mod = 11 відповідає формату "регістр-регістр".
3) Формат "регістр-безпосередній операнд" (3-4 байти): ----------- ------------- ---------- ----------------< br>
| КОП | s | w | | 11 | КОП "| reg | | непосред.операнд (1-2 б) |
----------- ------------- ----------------------- ---
Команди цього формату описують операції reg: = reg_immed (immed - безпосередній операнд). Біт w вказує на розмір операндів, а поле reg - на регістр-операнд (див. вище). Поле КОП в першому байті визначає лише клас операції (наприклад, клас додавання), уточнює ж операцію поле КОП "з другого байта. Безпосередній операнд може займати 1 або 2 байти - залежно від значення біта w, при цьому операнд-слово записується в команді в "перевернутому" вигляді. Задля економії пам'яті в ПК передбачений випадок, коли в операції над словами безпосередній операнд може бути заданий байтом (на цей випадок вказує 1 в бите s при w = 1), і тоді перед виконанням операції байт автоматично розширюється (зі знаком) до слова.
4) Формат "пам'ять-безпосередній операнд" (3-6 байт):
----------- -------------- -------------- ----------- -------< br>
| КОП | s | w | | mod | КОП "| mem | | адреса (0-2б) | | непоср.оп (1-2б) |
----------- -------------- -------------- ----------- -------< br>
Команди цього формату описують операції типу mem: = mem_immed. Зміст всіх полів - той же, що і в попередніх форматах.
Крім розглянутих в ПК використовуються й інші формати команди з двома операндами; так, передбачений спеціальний формат для команд, один з операндів яких фіксований (зазвичай це регістр AX). Мають свої формати і команди з іншим числом операндів.
1.3.3 Запис команд в MASM
b> Зі сказаного ясно, що одна й та сама операція в залежності від типів операдов записується у вигляді різних машинних команд: наприклад, у ПК є 28 команд пересилання байтів і слів. У той же час в MASM
всі ці "родинні" команди записуються однаково: наприклад, всі команди пересилання мають одну і ту ж символьну форму запису:
MOV op1, op2 (op1: = op2)
Аналізуючи типи операндів, асемблер сам вибирає потрібну машинну команду.
У загальному випадку команди записуються в MASM наступним чином:
мітка: мнемокод операнди; коментар
Мітка з двокрапкою, а також крапку з комою і коментар можуть бути відсутні. Мітка грає роль імені команди, її можна використовувати у командах переходу на цю задачу. Коментар не впливає на зміст команди, а лише пояснює її.
Мнемонічні назви операцій повністю перераховані в розділі 2. Операнди, якщо є, перераховуються через кому. Основні правила
запису операндів наступні.
Регістри вказуються своїми іменами, наприклад:
MOV AX, SI; обидва операнда - регістри
Безпосередні операнди задаються константними виразами (їх значеннями є константи-числа), наприклад:
MOV BH, 5; 5 - безпосередній операнд
MOV DI, SIZE X; SIZE X (число байтів, займаних змін; ної X) - безпосередній операнд
Адреса описуються адресними виразами (наприклад, іменами змінних), які можуть бути модифіковані з одного або двох регістрів; наприклад, у таких командах перший операнди задають адреса:
MOV X, AH
MOV X [BX] [DI], 5
MOV [BX], CL
Під час запису команд в символьної формі необхідно уважно стежити за правильним зазначенням типу (розміру) операндів, щоб не було помилок. Тип звичайно визначається за зовнішнім виглядом одного з них, наприклад:
MOV AH, 5; пересилка байта, тому що AH - байтовий регістр
MOV AX, 5; пересилання слова, тому що AX - 16-бітовий регістр
; (операнд 5 може бути байтом і словом, за ним, не можна визначити розмір пересилається величини)
MOV [BX], 300; пересилання слова, тому що число 300 не може бути; байтом
Якщо за зовнішнім виглядом можна однозначно визначити тип обох операндів, тоді ці типи повинні збігатися, і?? аче асемблер зафіксує помилку. Приклади:
MOV DS, AX; обидва операнда мають розмір слова
MOV CX, BH; помилка: регістри CX і BH мають різні розміри
MOV DL, 300; помилка: DL - байтовий регістр, а число 300 не; може бути байтом
Можливі ситуації, коли за зовнішнім виглядом операндів не можна визначити тип жодного з них, як, наприклад, у команді
MOV [BX], 5
Тут число 5 може бути і байтом, і словом, а адреса з регістра BX може вказувати і на байт пам'яті, і на слово. У подібних ситуаціях асемблер фіксує помилку. Щоб уникнути її, треба уточнити тип одного з операндів за допомогою оператора з назвою PTR:
MOV BYTE PTR [BX], 5; пересилка байта
MOV WORD PTR [BX], 5; пересилання слова
(Оператори - це різновид виразів мови MASM, аналогічні функціям.)
Оператор PTR необхідний і в тому випадку, коли треба змінити тип, запропонований імені при його описі. Якщо, наприклад, X описано як ім'я змінної розміром в слово:
X DW 999
і якщо треба записати в байтовий регістр AH значення тільки першого байта цього слова, тоді скористатися командою
MOV AH, X
не можна, тому що її операнди мають різний розмір. Цю команду слід записати трохи інакше:
MOV AH, BYTE PTR X
Тут конструкція BYTE PTR X означає адреса X, але вже розглядається не як адресу слова, а як адреса байта. (Нагадаємо, що з одного і того ж адреси може починатися байт, слово і подвійне слово; оператор PTR
уточнює, осередок якого розміру ми маємо на увазі.)
І ще одне зауваження. Якщо в символьної команді, що оперує зі словами, вказаний безпосередній операнд розміром в байт, як, наприклад, у команді
MOV AX, 80h
то виникає деяка неоднозначність: що буде записано в регістр AX - число 0080h (+128) або 0FF80h (-128)? У подібних ситуаціях асемблер формує машинну команду, де операнд-байт розширений до слова, причому розширення відбувається зі знаком, якщо операнд був записаний як від'ємне число, і без знака в інших випадках. Наприклад:
MOV AX, -128; => MOV AX, 0FF80h (A: =- 128)
MOV AX, 128; => MOV AX, 0080h (A: = +128) MOV AX, 80h; => MOV AX, 0080h (A: = +128)
1.4. СЕГМЕНТІРОВНІЕ
p>
1.4.1 Сегменти пам'яті. Сегментні регістри.
b> Перші моделі ПК мали оперативну пам'ять об'ємом 2 ^ 16 байт (64Кб) і тому використовували 16-бітові адреси. У подальших моделях пам'ять була збільшена до 2 ^ 20 байт (1Мб = 1000Кб), для чого вже потрібні 20-бітові адреси. Однак у цих ПК заради збереження наступності були збережені 16-бітові адреси: саме такі адреси зберігаються в регістрах і вказуються в командах, саме такі адреси виходять в результаті модмфікаціі з базових та індексних регістрів. Як же вдається 16-бітовими адресами посилатися на 1Мб пам'яті?
Ця проблема вирішується за допомогою сегментації адрес (неявного базування адрес). У ПК вводиться поняття "сегмент пам'яті". Так називається будь-яка ділянка пам'яті розміром до 64Кб і з початковим адресою, кратним 16. Абсолютний (20-бітовий) адреса A будь-якої комірки пам'яті можна
представити як суму 20-бітового початкового адреси (бази) B сегмента, якому при