Ibm pc
ЯЗЫК МАКРОАССЕМБЛЕРА IBM PC (Довідкове посібник) Упорядник: В. Н. Пильщиков (МДУ, ВМК) (січень 1992 р.) У посібнику розглядається мову макроассеблера для персональних ЕОМ типу IBM PC (мову MASM, версія 4.0). Посібник складається з 4-х глав. У розділі 1 розглянуті особливості переклсональных комп’ютерів типу IBM PC і приведені початкові інформацію про мові MASM. У розділі 2 описується система… Читати ще >
Ibm pc (реферат, курсова, диплом, контрольна)
ЯЗЫК МАКРОАССЕМБЛЕРА IBM PC (Довідкове посібник) Упорядник: В. Н. Пильщиков (МДУ, ВМК) (січень 1992 р.) У посібнику розглядається мову макроассеблера для персональних ЕОМ типу IBM PC (мову MASM, версія 4.0). Посібник складається з 4-х глав. У розділі 1 розглянуті особливості переклсональных комп’ютерів типу IBM PC і приведені початкові інформацію про мові MASM. У розділі 2 описується система команд цих комп’ютерів. Глава 3 присвячена присвячена власне мови MASM. У розділі 4 наведено приклади фрагментів програм, тож повних програм на MASM на вирішення разособистих завдань. У посібнику не розглядаються питання, пов’язані з обробкою двоично-десятичных чисел і клубною роботою арифметичного співпроцесора 8087 чи 80 287. Під терміном «ПК «в посібнику розуміється персонального комп’ютера типу IBM PC з мікропроцесором 8088/8086, 80 186 чи 80 286. ГЛАВА 1. ОСОБЛИВОСТІ ПК. ЗАПРОВАДЖЕННЯ У MASM. 1.1. ОПЕРАТИВНА ПАМ’ЯТЬ. РЕГІСТРИ. 1.1.1 Оперативна пам’ять Обсяг оперативної пам’яті ПК — 220 байтів (1 МБ). Байти нумеруються починаючи з 0, номер байта називається його адресою. Для посилань на байти пам’яті використовуються 20-разрядные адреси: від 0 до FFFFF (в 16-ричіншої системі). Байт містить 8 розрядів (бітов), кожен із яких може принимати значення 1 чи 0. Розряди нумеруються справа-наліво від 0 до 7: 7 6 5 4 3 2 1 0 Байт — це найменша адресуемая осередок пам’яті. У ПК використовують і більші осередки — слова подвійні слова. Слово — це два сусідних байта, розмір слова — 16 бітов (вони нумеруються справа-наліво від 0 до 15). Адресою слова вважається адресу його першого байта (із меншим пеклоресом); ця адреса то, можливо четным і непарною. Подвійне слово — це будь-які чотири сусідніх байта (дві сусідні слова), розмір такий осередки — 32 біта; адресою подвійного слова вважається адресу його першого байта. Байти йдуть на зберігання невеликих цілих чисел і символів, слова — для зберігання цілих чисел і адрес, подвійні слова — для хранения «довгих «цілих чисел і створити т.зв. адресних пар (сегмент:смещение). 1.1.2 Регістри Крім осередків оперативної пам’яті для зберігання даних (щоправда, разівковременного) можна використовувати й регістри — осередки, що входять до sosтав процесора і пропонує доступні з машинної програми. Доступ до регістрам здійснюється значно швидше, ніж до осередків пам’яті, тому івкористування регістрів помітно зменшує час виконання програм. Усі регістри мають розмір слова (16 бітов), кожним їх задореплено певне ім'я (AX, SP тощо.). За призначенням і способу використання регістри може бути розбитий ми такі групи: — регістри загального призначення (AX, BX, CX, DX, BP, SI, DI, SP); - сегментні регістри (CS, DS, SS, ES); - лічильник команд (IP); - регістр прапорів (Flags). (Розшифровка про ці назви: A — accumulator, акумулятор; B — base, база; З — 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 тощо. (H — high, старший; L — low, молодший): —————- —————- —————- —————- AX | AH | AL | BX | BH | BL | CX | CH | CL | DX | DH | DL | —————- —————- —————- —————- 15 8 7 0 Отже, з кожним із цих регістрів можна працювати з єдиним цілим, а можна працювати з його «половинками ». Наприклад, можна записать слово в AX, та був вважати тільки п’яту частину слова з регістру AH чи замінити тільки п’яту частину в регістрі AL тощо. Таке пристрій регістрів дозволяє використовувати їх до роботи і з числами, і з символами. Решта регістри не діляться на «половинки », тому вважати чи записати їхній вміст (16 бітов) можна тільки повністю. Сегментні регістри CS, DS, SS і ES неможливо знайти операндами нікаких команд, крім команд пересилки і стековых команд. Ці регістри івкористуються ним тільки для сегментування адрес (див. 1.4). Лічильник команд IP завжди містить адресу (усунення з початку програми) тієї команди, що має бути виконано наступній (початок програми зберігається в регістрі CS). Вміст регістру IP можна изменитку лише командами переходу. 1.1.3 Прапори І, нарешті, в ПК є особливий регістр прапорів. Прапор — це біт, приймає значення 1 («прапор встановлено »), якщо виконано деяке умова, і значення 0 («прапор скинуто ») інакше. У ПК івкористується 9 прапорів, кожному їх присвоєно певне ім'я (ZF, CF тощо.). Усі їх зібрано в регістрі прапорів (кожен прапор — це з розрядів регістру, частину його розрядів немає): ————————————————————————- Flags | x| x| x| x|OF|DF|IF|TF|SF|ZF| x|AF| x|PF| x|CF| ————————————————————————- 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 Деякі прапори прийнято називати прапорами умов; вони автоматическі змінюються і під час команд і фіксують ті чи інші якості їх результату (наприклад, дорівнює він нулю). Інші прапори називаються флагами станів; вони змінюються з програми розвитку й впливають на далечнейшее поведінка процесора (наприклад, блокують переривання). Прапори умов: CF (carry flag) — прапор перенесення. Приймає значення 1, якщо додаванні цілих чисел з’явилася одиниця перенесення, не «влезающая «в разрядну сітку, або якщо при вирахуванні чисел без знака перше їх бло менше другого. У командах зсуву в CF заноситься біт, що вийшла за розрядну сітку. CF фіксує також особливості команди множення. OF (overflow flag) — прапор переповнення. Встановлюється один, якщо додаванні чи вирахуванні цілих чисел зі знаком вийшов результат модулю переважаючий допустиму величину (сталося переповнювання мантиси і її «залізла «в знаковий розряд). ZF (zero flag) — прапор нуля. Встановлюється один, якщо результат команди дорівнював 0. SF (sign flag) — прапор знака. Встановлюється один, тоді як операції над знаковими числами вийшов негативний результат. 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), чим можна воскористуватися при налагодженні програми на її трасування. 1.2. ВИСТАВУ ДАНИХ. АРИФМЕТИЧНІ ОПЕРАЦІЇ Тут розглядається машинне уявлення цілих чисел, рядків і адрес. Уявлення двоично-десятичных чисел, використовуваних достаточно рідко, не розглядається. Що ж до речовинних чисел, то ПК немає команд речовинної арифметики (операції над цими числами реализуются програмним шляхом, або виконуються співпроцесором) і тому немає стандартного уявлення речовинних чисел. З іншого боку, рассматриваются деякі особливості виконання арифметичних операцій. Шестнадцатиричные числа записуються букві h на кінці, двоичные числа — букві b (так вважають у MASM). 1.2.1 Уявлення цілих чисел. У випадку під ціла кількість можна відвести будь-яке число байтів, проте система команд ПК підтримує лише числа площею байт і словом, і частково підтримує числа площею подвійне слово. Саме це формати і буде розглянуті. У ПК робиться різницю між цілими числами без знака (неотрицательными) і з знаком. Це тим, що у осередках один і того самого розміру можна більший діапазон беззнаковых чисел, ніж неотрицательных знакових чисел, і якщо відомо заздалегідь, що недоторая числова величина є неотрицательной, то вигідніше рассматривать її як беззнакову, як тільки знакову. Цілі числа без знака. Ці числа можуть бути як байта, слова чи подвійного слова — залежно від своїх розміру. У нинішньому вигляді байта видаються цілі від 0 до 255 (=28−1), як слова — цілі від 0 до 65 535 (=216−1), як подвійного слова — цілі від 0 до запланованих 4 294 967 295 (=232−1). Числа записываются в двоичной системі числення, займаючи все розряди осередки. Наприклад, число 130 записується як байта 1 000 0010b (82h). Числа площею слово зберігаються у пам’яті в «перевернутому «вигляді: младщие (праві) 8 бітов числа розміщуються у першому байті слова, а старші 8 бітов — у другому байте (в 16-ричной системі: дві праві цифри — у першому байте, дві ліві цифри — у другому байте). Наприклад, число 130 (=0082h) як слова зберігається у пам’яті так: —————- | 82 | 00 | —————- (Зазначимо, проте, що у регістрах числа зберігаються у нормальному вигляді: —————- AX | 00 | 82 | —————- AH AL) «Перевернуте «уявлення використовують і при зберіганні у пам’яті цілих чисел площею подвійне слово: у першому його байте розміщуються молодші 8 бітов числа, у другому байті - попередні 8 бітов тощо. Наприклад, число 1 234 5678h зберігається у пам’яті так: ——————————- | 78 | 56 | 34 | 12 | ——————————- Інакше кажучи, у першому слові подвійного слова розміщуються молодші (праві) 16 бітов числа, тоді як у другому слові - старші 16 бітов, причому у кожному з цих двох слів своєю чергою використовується «перевернуте «уявлення. Таке незвичне уявлення чисел пояснюється лише тим, що у перших моделях ПК за один раз можна було з пам’яті лише одне байт і всі арифметичні операції над багатозначними числами розпочинаються з дій над молодшими цифрами, тому з пам’яті насамперед треба зчитувати молодші цифри, якщо одразу ж не вважається все цифри. Зважаючи на це, у перших ПК і вони розміщувати молодші цифри числа перед старшими цифрамми, а заради наступності таке уявлення чисел зберегли у наступних моделях ПК. Звісно, «перевернуте «уявлення незручно для таких людей, однак за використанні мови ассемблера це незручність відчувається: в MASM все числа записуються нормального, неперевернутом вигляді (див. ніж). Цілі числа зі знаком. Ці числа також видаються як байта, слова подвійного слова. У нинішньому вигляді байта записуються числа від -128 до 127, як слова — числа від -32 768 до 32 767, а вигляді подвійного слова — числа від -2 147 483 648 до 2 147 483 647. У цьому числа записуються в дополнительном коді: ненегативне число записується як і, як і беззнаковое число (тобто. у прямому коді), а негативне числоx (x>0) представляется беззнаковым числом 28-x (для байтів), 216-x (для слів) чи 232-x (для подвійних слів). Наприклад, додатковим кодом числа -6 является байт FAh (=256−6), слово FFFAh чи подвійне слово FFFFFFFAh. У цьому байт 1 000 0000b (=80h) сприймається як -128, ніж як +128 (слово 8000h тлумачать як -32 678), тому лівий біт додаткового коду завжди ж виконує функцію знакового: для неотрицательных чисел він дорівнює 0, для негативних — 1. Знакові числа площею словом, і подвійне слово записуються в памяти в «перевернутому «вигляді (у своїй знаковий біт перебувають у селледнем байте осередки). Однак у MASM ці числа, як і беззнаковые, записываются за нормальної формі. Іноді число-байт необхідно розширити до слова, тобто. потрібно підлозічить таку ж за величиною число, але площею слово. Існує дві способу такого розширення — без знака і з знаком. У кожному разі івходное число-байт потрапляє на другий (до «перекидання ») байт слова, тоді як перший байт заповнюється по-різному: у результаті розширення без знака до нього записуються нульові біти (12h -> 0012h), а у результаті розширення зі знаком у байт записуються нулі, якщо число-байт було неотрицательным, і записується вісім двійкових одиниць на іншому разі (81h -> FF81h). Інакше кажучи, у результаті розширення зі знаком у першому байті слова копіюється знаковий розряд числа-байта. Аналогічно відбувається розширення числа-слова до подвійного слова. 1.2.2 Особливості виконання арифметичних опреаций У ПК є команди складання і вирахування цілих чисел площею словом, і байт. Спеціальних команд для складання і вирахування подвійних нічого не скажеш, ці операції реалізуються через команди складання і вирахування слів. Складання і віднімання беззнаковаых чисел проводиться у разі модулю 28 для байтів і 216 для слів. Це означає, що у результаті сложения з’явилася одиниця перенесення, не вмещающаяся в розрядну сітку, вона відкидається. Наприклад, при додаванні байтів 128 і 130 виходить число 258 = 10 000 0010b, тому ліва двоичная одиниця відкидається і залишається число 2 = 10b, що й оголошується результатом складання. Помилка не фіксується, але у прапор перенесення CF записується 1 (єсчи перенесення був, в CF заноситься 0). «Впіймати «таке спотворення торбми можна тільки наступним аналізом прапора CF. Спотворення результату є і при віднімання з меншої кількості більшого. І не фіксується помилка, проте першого числа дається «позику одиниці «(у разі байтів їх кількість поповнюється 256, для слів — на 216), після чого став і виробляється віднімання. Наприклад, вычитание байтів 2 і трьох зводиться до віднімання чисел 256+2=258 і трьох, в результаті чого виходить неправильна різницю 255 (а чи не -1). Щоб можна було побачити цю ситуацію, в прапор перенесення CF заалесится 1 (якщо позики був, в CF записується 0). Складання і віднімання знакових цілих чисел проводиться у разі тим самим алгоритмам, що у беззнаковых чисел (у тому одна з достоїнств дополнительного коду): знакові числа розглядаються як відповідающие беззнаковые числа, произодится операція над цими беззнаковыми числами і отриманого результату інтерпретується як знакову число. Например, складання байтовых чисел 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 = = 10 000 1001b, що становить додатковий код числа -127 (=256−129). Хоча результат тут вийшов і неправильним, процесор не фіксує помилку, зате заносить 1 в прапор переповнення OF (якщо «переповнення мантиси «був, в OF записується 0). Аналізуючи потім цей прапор, можна «впіймати «таку помилку. Отже, складання (віднімання) знакових і беззнаковых чисел проводиться у разі одному й тому алгоритму. У цьому ПК не «знає «, які числа (зі знаком чи ні) він складає; у разі він складывает їх як беззнаковые числа і у будь-якому разі формує прапори CF і OF. Ось як інтерпретувати складові суму, який з цих прапорів зважати — це справа автора програми. Що ж до множення і розподілу знакових і беззнаковых чисел, всі вони виконуються з різних алгоритмам, різними машинними командами. Однако і в операцій є низка особливостей. При множенні байтів (слів) перший множене зобов’язаний перебувати у регістрі AL (AX), результатом ж множення є слово (подвійне слово), яке заалесится в регістр AX (регістри DX і AX). Тим самим було при множенні сохраняются все цифри твори. При розподілі байтів (слів) перший оперинд (подільне) може бути словом (подвійним словом) і зобов’язаний перебувати у регістрі AX (регістрах DX і AX). Результатом розподілу є дві величини площею байт (слово) — неповне приватне (divx) і залишок від розподілу (mod); неповне приватне записується в регістр AL (AX), а остаток — в регістр AH (DX). 1.2.3 Уявлення символів і рядків На символ відводиться один байт пам’яті, куди записується код символу — ціле від 0 до 255. У ПК використовується система кодування ASCII (American Standard Code for Information Interchange). Вона, природнийале, зовсім позбавлений кодів російських літер, у нашій країні застосовується певний варіант цією системою з російськими літерами (зазвичай альтернативная кодування Держстандарту). Деякі особливості цих систем кодування: — код прогалини менше коду будь-який літери, цифри і взагалі будь-якого графически представимого символу; - коди цифр упорядковані за величиною цифр і містять перепусток, тобто. з нерівності код («0 »)+; наприклад, для доступу до байту із кількістю -5 слід зазначити вираз G+1, для доступу до байту з 10h — вираз G+2 тощо. Якщо директиві DB перелічую лише символи, наприклад: P. S DB «a », «+ », «b «цю директиву можна записати коротше, уклавши всі ці символи один лапки: P. S DB «a+b «І, нарешті, тоді як директиві описується кілька однакових кінстант (змінних), можна скористатися конструкцією повторення k DUP (a, b,…, c) яка еквівалентна повтореної k раз послідовності a, b,…, c. Наприклад, директиви 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. ВИСТАВУ КОМАНД. МОДИФІКАЦІЯ АДРЕС. 1.3.1 Структура команд. Виконавчі адреси Машинні команди ПК займають від 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. За виконання команди процесор передусім обчислює т.зв. івполнительный (ефективний) адресу — як сукупність адреси, заданого в доманде, і поточних значень зазначених регистров-модификаторов, причому всі ці величини розглядаються як неотрицательные і підсумовування ведеться за модулю 216 ([r] означає вміст регістру r): A: Aисп = A A[M]: Aисп = A+[M] (mod 216) A[M1][M2]: Aисп = A+[M1]+[M2] (mod 216) Отриманий в такий спосіб 16-разрядный адресу визначає т.зв. смещение — адресу, відлічений з початку деякого сегмента (області) памяти. Перед зверненням до пам’яті процесор ще додає до зрушення начальный адресу цього сегмента (він зберігається у певній сегментном регистре), у результаті виходить остаточний 20-разрядный пеклорес, за яким відбувається реальне звернення до пам’яті (див. 1.4). 1.3.2 Формати команд У ПК формати машинних команд досить різноманітні. Наприклад наведемо лише основні формати команд з цими двома операндами. 1) Формат «регистр-регистр «(2байта): ——————- ———————— | КОП |d|w| | 11 |reg1|reg2| ——————- ———————— 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 ————————- ————————- 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 байта): ——————- ——————- —————————- | КОП |d|w| |mod|reg|mem| |адресу (0−2 байта)| ——————- ——————- —————————- Ці команди описують операції 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 ———————————————————————————- 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 байта): —————- ——————- ————————————— | КОП |s|w| |11|КОП «|reg| |непосред.операнд (1−2 б)| —————- ——————- ————————————— Команди цього формату описують операції reg:=reg#immed (immed — непосередній операнд). Биток w свідчить про величину операндов, а полі reg — на регистр-операнд (див. вище). Поле КОП у першому байті определяет лише клас операції (наприклад, клас складання), уточнює ж операцию полі КОП «з другого байта. Безпосередній операнд може занімати 1 чи 2 байта — залежно від значення біта w, у своїй операнд-слово записується у команді в «перевернутому «вигляді. Заради економії пам’яті в ПК передбачено випадок, як у операції над словами непосредственный операнд може бути поставлене байтом (цей випадок вказує 1 в бите p. s при w=1), і тоді перед виконанням операції байт автоматически розширюється (зі знаком) до слова. 4) Формат «память-непосредственный операнд «(3−6 байтів): —————- ——————— ——————— ————————— | КОП |s|w| |mod|КОП «|mem| |адресу (0−2б)| |непоср.оп (1−2б)| —————- ——————— ——————— ————————— Команди цього формату описують операції типу mem:=mem#immed. Сенс всіх полів — хоча б, що у попередніх форматах. Крім розглянутих ПК використовують і інші формати команди з цими двома операндами; так, є спеціальний формат для команд, одне із операндов яких фіксований (зазвичай регістр AX). Мають свої формати і команди з іншим числом операндов. 1.3.3 Запис команд в MASM З сказаного ясно, що одне й та операція залежно від тизаговорили українською у операдов записується у різноманітних машинних команд: наприклад, в ПК є 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. СЕГМЕНТИРОВНИЕ 1.4.1 Сегменти пам’яті. Сегментні регістри. Перші моделі ПК мали оперативну пам’ять обсягом 216 байтів (64Кб) і тому використовували 16-битовые адреси. У наступних моделях пам’ять було збільшено до 220 байтів (1Мб=1000Кб), навіщо вже необходимы 20-битовые адреси. Однак цих ПК для збереження преемственности було збережено 16-битовые адреси: саме такі адреси зберігаються у регістрах і вказуються в командах, саме такі адреси виходять внаслідок модмфикации з базових і індексним регістрам. Які ж удается 16-битовыми адресами посилатися на 1Мб пам’яті? Проблема вирішується питання з допомогою сегментування адрес (неявного базування адрес). У ПК вводиться поняття «сегмент пам’яті «. Так, называется будь-яку ділянку пам’яті розміром до 64Кб і з початковим адресою, кратним 16. Абсолютний (20-битовый) адресу A будь-який осередки пам’яті можна видати за суму 20-битового початкового адреси (бази) B сегмента, якому належить осередок, і 16-битового усунення D — адреси цієї осередки, отсчитанного з початку сегмента: A=B+D. (Неоднозначність выбора сегмента не відіграє ролі, головне — щоб сума B і D давала потрібний адресу.) Адреса B заноситься в певний регістр P. S, а доманде, де вона має бути зазначений адресу A, замість нього записується пара з регістру P. S і усунення D (в MASM така пара, звана адресної парій чи покажчиком, записується як S: D). Процесор ж, влаштований отже і під час команди він передовсім Рижукових по парі S: D обчислює абсолютный адресу A як сукупність вмісту регістру P. S і усунення D і лише для того звертається до пам’яті за цією адресою A. Отак, замінюючи в командах абсолютні адреси на адресні пари, і адресувати всю пам’яти 16-битовыми адресами (зміщеннями). Як регістру P. S дозволяється використовувати не будь-який регістр, а один з 4 регістрів, званих сегментными: CS, DS, SS і ES. У зв’язку з цим одночасно можна працювати із чотирьох сегментами пам’яті: початок однієї з них завантажується в регістр CS і всі на осередки цього сегмента вказуються як пар CS: D, початок наступного заноситься в DS і всі з його осередки задаються як пар DS: D тощо. Якщо одновременно треба працювати з великою кількістю сегментів, тоді доведеться своевременно рятувати вміст сегментних регістрів і нотувати у них як начальные адреси п’ятого, шостого тощо. сегментів. Зазначимо, використовуються сегменти можуть лежати у пам’яті довільним чином: можуть не перетинатися, а можуть перетинатися й навіть збігатися. Які сегменти пам’яті використовувати, у яких сегментных регістрах зберігати початкові адреси — усе це особисту справу автора машинної програми. Як і регістри ПК, сегментні регістри мають розмір слова. Поцьому виникає запитання: як розмістити у них 20-битовые начальные адреси сегментів пам’яті? Відповідь такий. Оскільки ці адреси кратні 16 (див. вище), то них молодші 4 біта (остання 16-ричная цифра) завжди нульові, тому ці біти годі й зберігати явно, а лише розуміти. Саме так робиться: в сегментном регістрі завжди зберігаються лише 16 бітов (перші чотири 16-ричные цифри) начального адреси сегмента (їх кількість називається номером сегмента чи навіть сегментом). При обчисленні ж абсолютного адреси A по парі S: D процесор спочатку приписує справа до регістру P. S чотири нульових біта (інакше кажучи, множить на 16) і потім додає усунення D, причому підсумовування ведеться за модулю 220: Aабс = 16*[S]+D (mod 220) Якщо, наприклад, в регістрі CS зберігається величина 1234h, тоді адресна пара 1234h:507h визначає абсолютний адресу, рівний 16*1234h+507h = 12340h+507h = 12847h. 1.4.2 Сегментні регістри за умовчанням Відповідно до описаної схемою сегментування адрес, заміну абсолютных адрес на адресні пари мають провадитися переважають у всіх командах, мающих операнд-адрес. Проте розробники ПК придумали спосіб, дозволяющий уникнути виписування таких пар переважно команд. Суть у тому, що заздалегідь домовляються у тому, який сегментний регістр на кадідька лисого сегмент пам’яті буде вказувати, і у командах задається лише усунення: не зазначений явно сегментний регістр автоматично восстанавливается відповідно до цієї домовленості. І лише за необхідності порушити цю домовленість треба повністю вказувати адресну пару. Що за домовленість? Вважається, що регістр CS завжди свідчить про початок області памяти, у якій розміщені команди програми (ця галузь називається пєгментом команд чи сегментом кодів), і тому при посиланнях на осередки цій галузі регістр CS годі й вказувати явно, він мається на увазі за умовчанням. (Зазначимо принагідно, що абсолютний адресу черговий команди, підлягає виконання, завжди задається парою CS: IP: в лічильнику команд IP завжди знаходиться усунення цієї команди щодо адреси з регистра CS.) Аналогічно передбачається, що регістр DS свідчить про пєгмент даних (область пам’яті з константами, перемінними та інші величинами програми), і тому під всіх посиланнях цей сегмент регістр DS можна року вказувати, т.к. він мається на увазі за умовчанням. Регістр SS, вважається, свідчить про стік — область пам’яті, доступом до якій здійснюється за принципом «останнім записано — першим лічений «(див. 1.7), і тому його посилання стік, у яких року зазначений сегментний регістр, за умовчанням сегментируются по регістру SS. Регістр ES считается вільним, не прив’язаний якого сегменту пам’яті і можна використовувати на власний розсуд; найчастіше його для доступа до даних, які помістилися чи свідомо були размещены у сегменті даних. З урахуванням такого розподілу ролей сегментних регістрів машинні програми зазвичай будуються так: все команди програми розміщуються в одном сегменті пам’яті, початок якого заноситься в регістр CS, проте дані розміщуються й інші сегменті, початок якого заноситься в регистр DS; якщо потрібен стік, то під нього відводиться третій сегмент памяти, початок якого записується в регістр SS. Після цього практично переважають у всіх командах можна вказувати не повні адресні пари, а лише смещения, т.к. сегментні регістри у тих парах будуть відновлюватися автоматично. Тут, щоправда, виникає таке питання: як у зміщення визначити, який сегмент пам’яті воно вказує? Точної відповіді наведено нижче (див. 1.4.3), а найзагальніших рисах він такий: посилання сегмент команд може лише в командах переходу, а посилання практично у всіх інших командах (крім строковых і стековых) — це посилання сегмент даних. Например, у команді пересилки MOV AX, X ім'я X сприймається як посилання дане, тому автоматично восстанавливается до адресної пари DS: X. У команді ж безумовного переходу за адресою, що у регістрі BX, JMP BX абсолютний адресу переходу визначається парою CS:[BX]. Отже, тоді як засланні на якусь осередок пам’яті не зазначений явно пєгментный регістр, цей регістр з вмовчанням. Явно ж сегментные регістри треба вказувати, лише коли із якихось причин регістр за умовчанням не підходить. Якщо, наприклад, у команді пересилки потрібно послатися на стік (скажімо, треба записати в регістр AH байт стека, помічений ім'ям X), тоді нас нічого очікувати влаштовувати домовленість у тому, що у вмовчанням операнд команди MOV сегментируется по регістру DS, і тому ми маємо явно вказати інший регістр — у разі регистр SS, т.к. саме його свідчить про стік: MOV AH, SS: X Але такі випадки трапляються нечасто і тому командах, як правило, вказуються лише усунення. Зазначимо, що у MASM сегментний регістр записується у самій команде безпосередньо перед зміщенням (ім'ям перемінної, міткою тощо.), проте, попри рівні машинного мови ситуація трохи інакша. Є 4 спеціальні однобайтовые команди, звані префіксами заміни сегмента (обозначаемые як CS, DS, SS: і ES:). Вони ставляться перед командой, операнд-адрес якої повинен бути просегментирован по регістру, що відрізняється від регістру, подразумеваемому за умовчанням. Наприклад, приведенная вище символічна команда пересилки — це насправді дві машинні команди: SS: MOV AH, X 1.4.3 Сегментування, базування і індексування адрес Оскільки сегментування адрес — це різновид модифікації адрес, то ПК адресу, указываемый у команді, у випадку модифицируется по трьох регістрам — сегментному, базовому і індексному. У цебрухт, модифікація адреси виробляється у два етапу. Спочатку враховуються лише базовий і індексний регістри (якщо вони, звісно, зазначені у доманде), причому обчислення відбувається у сфері 16-битовых адресов; отриманий у результаті 16-битовый адресу називається виконавецьным (ефективним) адресою. Якщо команді не передбачено звернення до пам’яті (наприклад, вона завантажує адресу в регістр), то, на цьому модификация адреси завершується і використовується саме виконавчий адресу (він завантажується в регістр). Якщо потрібен доступом до пам’яті, тоді другого етапу виконавчий адресу сприймається як усунення і до нему додається (помножена на 16) вміст сегментного регістру, зазначеного явно чи взятого за умовчанням, у результаті виходить абсолютний (фізичний) 20-битовый адресу, яким це реально і происходить звернення до пам’яті. Зазначимо, що сегментний регістр враховується лише у «останній «момент, безпосередньо перед зверненням до пам’яті, а доти робота ведеться тільки з 16-битовыми адресами. Коли ж врахувати при цьому, що пєгментные регістри, зазвичай, не вказуються в командах, можна загалом вважати, що ПК працює із 16-битовыми адресами. Як сказано, тоді як засланні на осередок пам’яті не зазначений сегментный регістр, він визначається за умовчанням. Це потрібно по ідущим правилам. 1) У командах переходу адресу переходу сегментируется по регістру CS і лише з нього, т.к. абсолютний адресу команди, що має бути виконано наступній, завжди визначається парою CS: IP (спроба зменить в командах сегментний регістр буде безуспішною). Зазначимо, що сегментиорвание по регістру CS стосується саме адреси переходу, а чи не адреси тієї осередки, де він може перебувати. Наприклад, у команді безумовного переходу за адресою, що у осередку X: JMP X ім'я X сегментируется по регістру DS, тоді як адресу переходу, узятий із осередки X, вже сегментируется по регістру CS. 2) Адреси у всіх інших командах, крім строковых (STOS, MOVS, SCAS і CMPS), за умовчанням сегментируются: — по регістру DS, якщо серед зазначених регистров-модификаторов немає регістру BP; - по регістру SS, якщо з модифікаторів — регістр BP. Отже, адреси виду A, A[BX], A[SI], A[DI], A[BX][SI] і A[BX][DI] сегментируются по регістру DS, а адреси A[BP], A[BP][SI] і A[BP][DI] - по регістру SS, тобто. адреси трьох останніх видів используются для доступу до осередків стека. 3) У строковых командах STOS, MOVS, SCAS і CMPS, мають два операнда-адреса, куди вказують індексні регістри SI і DI, одне із операндов (яку підказує SI) сегментируется по регістру DS, а інший (нею вказує DI) — по регістру ES. 1.4.4 Програмні сегменти. Директива ASSUME Розглянемо, як сегментування проявляється у програмах на MASM. Щоб вказати, що деяка група пропозицій програми на MASM утворюють єдиний сегмент пам’яті, вони оформляються як програмный сегмент: їх ставиться директива SEGMENT, після нього — директива ENDS, причому у початку обох цих директив має бути вказано одале те ж ім'я, відіграватиме роль імені сегмента. А програма загалом є послідовність таких програмних сегментів, наприкінці якій вказується директива кінця програми END, наприклад: DT1 SEGMENT ;програмний сегмент безпосередньо з ім'ям DT1 A DB 0 B DW? DT1 ENDS; DT2 SEGMENT ;програмний сегмент DT2 З DB «hello «DT2 ENDS; CODE SEGMENT ;програмний сегмент CODE ASSUME CS: CODE, DS: DT1, ES: DT2 BEG: MOV AX, DT2 MOV DS, AX MOV BH, C … CODE ENDS END BEG ;кінець тексту програми Пропозиції програмного сегмента асемблер розміщає щодо одного пєгменте пам’яті (разом вони повинні займати більш 64Кб) начиная з найближчого вільного адреси, кратного 16. Номер (перші 16 бітов початкового адреси) цього сегмента стає значенням імені пєгмента. У MASM це належить до константним выражениям, а чи не адресуным, у зв’язку з ніж у команді MOV AX, DT2 другий операнд є безпосереднім, у регістр AX прописано початок (номер) сегмента DT2, а чи не вміст початковій осередки цього сегмента. Імена ж змінних (A, B, З) і мітки (BEG) ставляться до адресним выражениям, і це ставлять у відповідність адресу їх осередки щодо «свого «сегмента: імені A відповідає адресу 0, імені B — адресу 1, імені З — адресу 0, а мітці BEG — адресу 0. Усі посилання пропозиції одного програмного сегмента асемблер сегментирует за умовчанням за одним й тому сегментному регістру. За яким саме — встановлюється спеціальної директивою ASSUME. У прикладі ця директива визначає, що це посилання сегмент CODE должны, якщо року зазначений сегментний регістр, сегментуватися по регистру CS, все посилання DT1 — по регістру DS, проте посилання DT2 — по регістру ES. Зустрівши з тексту програми посилання якесь ім'я (наприклад, з ім'ям З у команді MOV AX, C), асемблер визначає, що не програмному сегменті воно описано (ми — в DT2), потім інформації з директиви ASSUME дізнається, який сегментний регістр поставлене відповідність цього сегмента (ми — це ES), і далі утворює адресну пару иэ даного регістру і усунення імені (ми — ES:0), якою і записує в формируемую машинну команду. У цьому асемблер враховує використовуване у ПК угоду про сегментних регістрах за умовчанням: тоді як адресної паре, вибудуваної ним самим чи явно заданої у програмі, сегментний регистр збігаються з регістром за умовчанням, то машинну команду заалесится лише усунення. Якщо, скажімо, у нашій прикладі зустрінеться команда MOV CX, B, тоді з імені У асемблер побудує пару DS:1, але якщо операнд-адрес команди MOV за умовчанням сегментируется по регістру DS, то записувати цей регістр в машинну команду зайве і асемблер записывает у ній лише усунення 1. Отже, директива ASSUME рятує програмістів від необходимости виписувати повні адресні пари як тоді, коли используются сегментні регістри за умовчанням (як у безпосередньо з ім'ям B), але давайте тоді, як у машинної команді потрібно було б явно вказати сегментний регістр (як у безпосередньо з ім'ям З). У MASM сегментний регістр на засланні з ім'ям потрібно указувати лише тоді, коли ім'я має по будь-яким причин сегментуватися по регістру, що відрізняється від цього, що поставльон у відповідність всьому сегменту, у якому це описано. Однак це справедливе за дотримання наступних условій. По-перше, директива ASSUME повинна бути вказана перед першої домандой програми. Інакше асемблер, просматривающий текст програми згори донизу, нічого очікувати знати, як сегментувати імена з доманд, розташованих до цієї директиви, і тому зафіксує помилку. По-друге, в директиві ASSUME слід кожному сегменту ставити в соответствие сегментний регістр: якщо ассемблеру зустрінеться посилання ім'я з сегмента, якому відповідає ніякої сегментний регістр, він зафіксує помилку. Щоправда, в обох випадках можна запобігти помилки, але при цьому засланні необхідно явно вказувати сегментний регістр. 1.4.5 Початкова завантаження сегментних регістрів Директива ASSUME повідомляє ассмеблеру у тому, за якими регістрам він має сегментувати імена з яких сегментів, і «обіцяє «, що у цих регістрах перебуватимуть початкові адреси цих сегментів. Однадо завантаження цих адрес в регістри сама директива не здійснює. Зробити таку завантаження — обов’язок самої програми, з завантаження пєгментных регістрів це має починатися виконання програми. Робиться так. Бо у ПК немає команди пересилки безпосереднього операнда в сегментний регістр (а ім'я, тобто. початок, сегмента — це безпосереднійный операнд), то таку завантаження доводиться робити через якийсь другой, несегментный, регістр (наприклад, AX): MOV AX, DT1 ;AX:=начало сегмента DT1 MOV DS, AX ;DS:=AX Аналогічно завантажується й регістр ES. Завантажувати регістр CS на початку програми зайве: він, як і лічильник команд IP, завантажується операційній системою до того, як починається виконання програми (інакше там було розпочати його виконання). Що ж до регістру SS, використовуваного до роботи зі стеком, він может бути завантажений як і, як і регістри DS і ES, однак у MASM предусмотрена можливість завантаження цього регістру ще до його виконання программы (див. 1.7). 1.4.6 Посилання вперед Зустрічаючи в символьній команді заслання тому — ім'я, яке описано з тексту програми до цієї команди, асемблер вже необхідну інформацію про ім'я і тому може правильно оттранслировать цю доманду. Але тоді як команді зустрінеться посилання вперед, тобто. ім'я, яке було описане до команди, і яке, напевно, буде описано пізніше, то асемблер здебільшого зможе правильно оттранслировать цю команду. Наприклад, не знаючи, що не програмному сегменті буде описано це, асемблер неспроможна визначити, з якого сегментному регістру треба сегментувати ім'я, і тому неспроможна визначити, треба чи ні розміщувати перед відповідної машинної командою префікс заміни сегмента і, коли треба, то що саме. У ситуації асемблер діє так: тоді як команді зустрілася посилання вперед, він робить деяке предположение щодо це ім'я вже з урахуванням цього припущення формирует машинну команду. Якщо потім (коли зустрінеться опис імені) виявиться, що це припущення було неправильним, тоді асемблер пытается виправити сформированнную ним раніше машинну команду. Але це який завжди вдається: якщо правильна машинна команда повинна займати більше, ніж машинна команда, побудована з урахуванням предположения (наприклад, команді треба насправді вставити префікс заміни сегмента), тоді асемблер фіксує помилку (зазвичай, це помилка номер 6: Phase error between passes.) Які ж припущення робить асемблер, зустрічаючи заслання вперед? В усіх життєвих командах, крім команд переходу (про неї див. 1.5), асемблер передбачає, що ім'я буде описано у сегменті даних, і тому сегментируется по регістру DS. Це треба враховувати під час упорядкування программы: тоді як команді зустрічається посилання вперед з ім'ям, яке описаале у сегменті, початку якого вказує сегментний регістр, отличный від DS, та над такою назвою автор програми повинен написати соотвествующмй префікс. Приклад: code segment assume cs: code x dw? beg: mov ax, x ;тут замість cs: x можна записати просто x mov cs: y, ax ;тут неодмінно треба записати cs: y … y dw? code ends 1.5. ПЕРЕХОДИ У систему команд ПК входить звичайний для ЕОМ набір команд переходу: безумовні і умовні переходи, переходи з поверненнями та інших. Однак у ПК ці команди мають деякі особливості, що тут і рассматриваются. Абсолютний адресу команди, що має бути виконано наступній, визначається парою CS: IP, тому виконання переходу означає зрадіние цих регістрів, обох або тільки одного (IP). Якщо змінюється лише лічильник команд IP, такий перехід називається внутрисегментным чи близьким (управління залишається у тому сегменті команд), і якщо меняются обидва регістру CS і IP, це межсегментный чи далекий перехід (починають виконуватися команди з іншого сегмента команд). По способу зміни лічильника команд переходи діляться на абсолютні і относительные. Якщо команді переходу зазначений адресу (усунення) тієї команди, доторою треба передати управління, це абсолютний перехід. Однак у команді то, можливо зазначена величина (зрушення), і треба додати поточному значенням регістру IP, щоб вийшов адресу переходу, і тоді це завжди буде відносний перехід; у своїй зрушення то, можливо положительным і негативним, отже можливий перехід уперед і тому. По величині зсуву відносні переходи діляться на короткі (зрушення задається байтом) і довгі (зрушення — слово). Абсолютні ж переходи діляться на прямі й опосередковані: з прямою переході адресу переходу поставив у самій команді, а при непрямому — у команді вказується регістр чи ячейка пам’яті, у якому (якої) перебуває адресу переходу. 1.5.1 Безумовні переходи. У MASM все команди безумовного переходу позначаються однаково: JMP op але у залежність від типу операнда, асемблер формує різні машинні команди. 1) Внутрисегментный відносний короткий перехід. JMP i8 (IP:=IP+i8) Тут i8 позначає безпосередній операнд розмірів в байт, який інтерпретується як знакову ціле від -128 до 127. Команда додає їх кількість до поточному значенням регістру IP, одержуючи у ньому адресу (смещение) тієї команди, що має бути виконано наступній. Регістр CS у своїй не змінюється. Необхідно враховувати таку особливість регістру IP. Виконання будь-який команди починається сіло, що у IP заноситься адресу наступній з ним команди, і лише для того виконується власне команда. Для команды відносного переходу це, що операнд i8 додається немає адресою цієї команди, а до адресою команди, наступній з ним, поэтому, приміром, команда JMP 0 — це перехід для наступної команду програми. Під час написання машинної програми зрушення для відносних переходов доводиться вираховуватимуть вручну, проте MASM позбавляє цьогоприємного заняття: в MASM в командах відносного переходу завжди вказується мітка тієї команди, яку треба передати управління, і асемблер сам обчислює зрушення, який і записує в машинну доманду. Звідси випливає, що у MASM команда переходу по мітці воспрининудиться не як абсолютний перехід, бо як відносний. По короткому переходу можна передати управління лише з ближайшие команди програми — віддалені від команди, наступній за командою переходу, до 128 байтів тому, чи до 127 байтів вперед. Для переходу більш далекі команди використовується 2) Внутрисегментный відносний довгий перехід. JMP i16 (IP:=IP+i16) Тут i16 позначає безпосередній операнд площею слово, который розглядається як знакова ціле від -32 768 до 32 767. Цей перехід аналогічний короткому переходу. Зазначимо, що, зустрівши команду переходу з міткою, якої був помічена одне з попередніх (за текстом) команд програми, асемблер вичисляет різницю між адресою цієї мітки і адресою команди переходу і з цього зрушенню визначає, яку машинну команду відносного перехода — коротку чи довгу — треба сформувати. Але якщо мітка ще зустрічалася з тексту програми, тобто. робиться перехід вперед, тоді асемблер, не знаючи ще адреси мітки, неспроможна визначити, яку саме машинну команду відносного переходу формувати, й тому він про всяк випадок вибирає команду довгого переходу. Однак це машинна команда займає 3 байта, тоді як команда короткого переходу — 2 байта, і якщо автор програми на MASM прагне економії пам’яті та чудово знає заздалегідь, що перехід вперед буде близьку мітку, він маю повідомити звідси ассемблеру, щоб він сформував команду короткого переходу. Таке вказівку робиться з допомогою оператора SHORT: JMP SHORT L Для переходів тому оператор SHORT непотрібен: вже знаючи адресу мітки, ассемблер сам визначить вид команди відносного переходу. 3) Внутрисегментный абсолютний непрямий перехід. JMP r16 (IP:=[r]) чи JMP m16 (IP:=[m16]) Тут r16 позначає будь-який 16-битовый регістр загального призначення, а m16 — адресу слова пам’яті. У цьому вся регістрі (слові пам’яті) повинен бути адресу, за яким буде зроблено перехід. Наприклад, за командою JMP BX осушествляется перехід за адресою, що у регістрі BX. 4) Межсегментный абсолютний прямий перехід. JMP seg: ofs (CS:=seg, IP:=ofs) Тут seg — початок (перші 16 бітов початкового адреси) деякого пєгмента пам’яті, а ofs — усунення у цьому сегменті. Кілька seg: ofs определяет абсолютний адресу, яким робиться перехід. У MASM ця пара завжди задається конструкцією FAR PTR, яка «каже », що потрібно зробити перехід у зазначеній мітці, причому ця мітка — «далека », з іншого сегмента. Зазначимо, що асемблер сам визначає, що це сегмент, і саме підставляє в машинну команду його початок, тобто. seg. 5) Межсегментный абсолютний непрямий перехід. JMP m32 (CS:=[m32+2], IP:=[m32]) Тут під m32 розуміється адресу подвійного слова пам’яті, у якому находится пара seg: ofs, задающая абсолютний адресу, яким дана доманда має виконати перехід. Нагадаємо, що у ПК величини площею подвійне слово зберігаються у «перевернутому «вигляді, тому усунення ofs находится у першому слові подвійного слова m32, а усунення seg — у другому слові (за адресою m32+2). Команди межсегментного переходу використовуються тоді, коли команди програми розміщені над одному сегменті пам’яті, а кількох (напризаходів, команд так багато, що у сукупності вони займають більш 64Кб, тобто. більш максимальної величини сегмента пам’яті). При перехід з одного такого сегмента на другий потрібно змінювати як лічильник доманд IP, а й вміст регістру CS, завантажуючи за останній початковий адресу другого сегмента. Таке одночасне зміна обох цих регистров і роблять команди межсегментного переходу. При запис у MASM команд переходу треба враховувати, що можуть сприйматися неоднозначно. Скажімо, як сприймати команду JMP A — як по мітці A чи як за адресою, що зберігається у осередку безпосередньо з ім'ям A? З іншого боку, що це перехід — внутрисегментный чи межсегментный? Відповідь залежить від цього, як описано ім'я A, і зажадав від того, коли описано ім'я A — до чи ж після команди переходу. Нехай A описано до команди переходу («посилання тому »). Якщо ім'ям A позначена деяка команда поточного сегмента команда (тобто. A — позначокка), тоді асемблер формує машинну команду внутрисегментного відносительного переходу. Якщо ж A — ім'я перемінної, тоді асемблер формує машинну команду непрямого переходу — внутрисегментного, якщо A описано в директиві DW, чи межсегментного, якщо A описано в директиві DD. У разі, якщо ім'я A описано після команди переходу («посилання вперед »), асемблер завжди формує машинну команду внутрисегментного відносного довгого переходу. З огляду на це ім'я A обов’язково має мітити команду з поточного сегмента команд, інакше буде зафиксирована помилка. Якщо таке трактування посилання вперед не задовольняє автора програми, тоді й зобов’язаний з допомогою оператора SHORT чи PTR уточнити тип імені A: JMP SHORT A ;внутрисегментный короткий перехід по мітці JMP WORD PTR A ;внутрисегментный непрямий перехід JMP DWORD PTE A ;межсегментный непрямий перехід Зазначимо, що перехід по мітці A з іншого сегмента команд завжди повинен указуватися з допомогою FAR PTR (незалежно від цього, описана позначокка A до чи помирають після команди переходу): JMP FAR PTR A ;межсегментный перехід по мітці 1.5.2 Умовні переходи. Практично в усіх командах умовного переходу перевіряється значение тієї чи іншої прапора (наприклад, прапора нуля ZF) і, якщо має певне значення, виконується перехід за адресою, зазначеному в доманде. Значення прапора має бути встановлено попередньої командою, наприклад, командою порівняння CMP op1, op2 яка обчислює різницю op1-op2, проте результат куди записывает, лише змінює прапори, куди і реагуватиме команда умовного переходу. У MASM команди умовного переходу мають таку форму: Jxx op де xx — одна чи кілька літер, у скороченому вигляді відбивають проверяемое умова (зазвичай, у припущенні, і цієї командою находится команда порівняння). Приклади деяких мнемоник: JE — перехід «по одно «(jump if equal) JL — перехід «по менше «(jump if less) JNL — перехід «по неменше «(jump if not less) Особеностью всіх машинних команд умовного переходу і те, що вони реалізують внутрисегментный відносний короткий перехід, тобто. додають до лічильника команд IP свій операнд, аналізований як знакову число від -128 до 127. У MASM цей операнд завжди повинен записываться як мітка, яку асемблер замінить на відповідний зрушення (див. вище). Така особливість команд умовного переходу викликає незручність при переходах на «далекі «команди. Наприклад, коли треба зробити перехід при A M (обхід команди JMP) JMP L ;менше —> L (довгий перехід) M: … 1.5.3 Команди управління циклом У ПК кілька команд, спрощують програмування циклів із числом повторень. Застосування цих команд вимагає, аби повернути до початку циклу в регістр CX було занесено число кроків циклу. Сами команди розміщуються наприкінці циклу, вони зменшують значення CX на 1 і, якщо CX ще одно 0, передають управління початку циклу. Напризаходів, знайти P. S — суму елементів масиву X з десяти чисел-слов ж личить отак: MOV AX, 0 ;початкова значення суми (накопичується в AX) MOV SI, 0 ;початкова значення індексного регістру MOV CX, 10 ;число повторень циклу L: ADD AX, X[SI] ;AX:=AX+X[i] ADD SI, 2 ;SI:=SI+2 LOOP L ;CX:=CX-1; if CX0 then goto L MOV S, AX ;S:=AX Крім команди LOOP є ще «циклічні «команди — LOOPZ і LOOPNZ (вони теж мають синонимичные назви LOOPE і LOOPNE), яких, крім регістру CX перевіряють що й прапор нуля ZF; наприклад, команда LOOPZ «виходить «з добірки, якщо CX=0 чи ZF=1. Цю команду можна, наприклад, івкористувати у пошуку в масиві першого нульового елемента, де має бути передбачено дві умови виходу з добірки: або перебуватиме знайдено нулівої елемент (ZF=1, якщо перед LOOPZ поставити команду порівняння очередного елемента з 0), або перебуватиме вичерпаний весь мсассив (CX=0) Зазначимо, всі ці «циклічні «команди реалізують короткий відносительный перехід, як і команди умовного переходу, тому їх можале використовувати лише циклів з гаком числом команд. У MASM є ще команди переходу — CALL (перехід із поверненням) і RET (повернення з підпрограми), вони розглядаються в 1.7. 1.6. СТРОКОВЫЕ ОПЕРАЦІЇ У ПК під рядком розуміється послідовність сусідніх байтів чи слів. У зв’язку з цим все строковые команди мають чи два різновиди — до роботи зі рядками з байтів (в мнемоніку операцій входить літера B) й у роботи з рядками з слів (в мнемоніку входить W). Є такі операції над рядками: — пересилання елементів рядків (на згадку про, з пам’яті, память-память); - порівняння двох рядків; - перегляд рядки в пошуках елемента, рівного заданому. Кожна з операцій виконується лише над одним елементом рядки, проте одночасно відбувається автоматична настроювання наступного року чи попередній елемент рядки. Є спеціальні команди повторення (REP та інших.), що змушують таку по них строковую команду багаторазово повторюватися (до 216 раз), у зв’язку з якщо така пара команд дозволяє обробити всю рядок, причому значно швидше, ніж запрограмований цикл. З іншого боку, рядки можна переглядати вперед (від своїх початку до кінцв) і. Напрям перегляду залежить від прапора напрями DF, значення можна змінювати з допомогою команд STD (DF:=1) і CLD (DF:=0). При DF=0 всі наступні строковые команди програми просматривают рядки вперед, а при DF=1 — тому. У строковых командах операнды року вказуються, а подразумеваются. Якщо команда працює із однієї рядком, то адресу чергового, прорабатываемого зараз елемента рядки задається парою регістрів DS і SI чи парою ES і DI, і якщо команда працює із двома рядками, то адресу елемента, а такою визначається парою DS: SI, а адресу елемента другой — парою ES: DI. По виконанні операції значення регістру SI і/або DI збільшується (при DF=0) чи зменшується (при DF=1) на 1 (для байтовых рядків) чи 2 (для рядків із слів). Початкова установка всіх таких регістрів, і навіть прапора DF має бути виконане на початок операції над рядком. Якщо сегментний регістр DS вже потрібне значення, тоді завантажити регістр SI з поміццю команди LEA SI, Якщо ж суспільства потрібно завантажити відразу обидва регістру DS і SI, можна буде воскористуватися командою LDS SI, m32 що у регістр SI заносить перше слово, а регістр DS — друге слово з подвійного слова, має адреc m32 (в такий спосіб, за адресою m32+2 повинен зберігатися сегмент, а, по адресою m32 — усунення початкового чи кінцевого елемента рядки). Початкову завантаження регістрів ES і DI зазвичай здійснюють однієї командою LES DI, m32 що діє аналогічно команді LDS. Перерахуємо коротенько строковые команди ПК. Команда завантаження елемента рядки у акумулятор (LODSB чи LODSW) пересилає в регістр AL чи AX черговий елемент рядки, яку підказує пара DS: SI, після чого збільшує (при DF=0) чи зменшує (при DF=1) регістр SI на 1 чи 2. Команда записи акумулятора в рядок (STOSB чи STOSW) заносить зідержимое регістру AL чи AX на той елемент рядки, яку підказує пара ES: DI, після чого змінює регістр DI на 1 чи 2. Команда пересилки рядків (MOVSB чи MOVSW) зчитує елемент першого рядка чудово, визначається парою DS: SI, в елемент другий рядки, определяемый парою ES: DI, після чого одночасно змінює регістри SI і DI. Команда порівняння рядків (CMPSB чи CMPSW) порівнює чергові елементи рядків, указываемые парами DS: SI і ES: DI, і результати сравнения (одно, менше (і т.п.) фіксує в прапорах, після чого змінює регистры SI і DI. Команда сканування рядки (SCASB чи SCASW) порівнює елемент рядки, адресу якого задається парою ES: DI, багатозначно регістру AL чи AX і результати порівняння фіксує в прапорах, після чого змінює зідержимое регістру DI. Перед будь-який строковой командою можна поставити жодну з двох доманд, званих «префіксами повторення », яка змусить багаторазово повторитися цю строковую команду. Кількість повторень (зазвичай довжина рядки) має зазначене в регістрі CX. Префікс повторення REPZ (синоніми — REPE, REP) спочатку заносить 1 в прапор нуля ZF, після чого, постійно зменшуючи CX на 1, змушує повторюватися таку його строковую команду до того часу, поки CX бракуватиме 0 або поки що прапор ZF не змінить своє значення на 0. Інший префікс повторення REPNZ (синоним — REPNE) діє аналогічно, але спочатку встановлює прапор ZF в 0, а при за зміни його за 1 припиняє повторення строковой команди. Приклад. Нехай треба переписати 10 000 байтів починаючи з адреси A деінде пам’яті починаючи з адреси B. Якщо обидва цих імені ставляться до сегменту даних, початку якого вказує регістр DS, цю пересилку можна зробити: CLD ;DF:=0 (перегляд рядки вперед) MOV CX, 1000 ;CX — число повторень MOV AX, DS MOV ES, AX ;ES:=DS LEA SI, A ;ES:SI — «звідки «LEA DI, B ;DS:DI — «куди «REP MOVSB ;пересилання CX байтів 1.7. СТІК. ПІДПРОГРАМИ. 1.7.1 Стік У ПК є спеціальні команди роботи з стеком, тобто. областю пам’яті, доступом до елементам якій здійснюється за принципом «послідним записано — першим лічений ». Але, щоб було воспользоваться цими командами, слід дотримуватися низки умов. Під стік можна відвести область будь-де пам’яті. Розмір її может бути будь-яким, але з повинен перевершувати 64Кб, та її початковий адресу може бути кратним 16. Інакше кажучи, ця галузь мусить бути пєгментом пам’яті; вона називається сегментом стека. Початок цього сегмента (перші 16 бітов початкового адреси) має неодмінно зберігатися в сегментном регістрі SS. Збережені в стеці елементи може мати будь-який розмір, проте треба враховувати, що у ПК є команди запис у стік читання потім із нього лише слів. Тож записи байта в стік їх треба попередньо розширити до слова, а запис чи читання подвійних слів здійснюються парою команд. У ПК прийнято заповнювати стік знизу вгору, від великих адрес до меншим: перший елемент записується насамкінець області, відведеної під стік, другий елемент — в попередню осередок області й т.д. Зчитується завжди елемент, записаний у стік останнім. У зв’язку з цим нижню межу стека завжди фіксована, а верхня — змінюється. Слово пам’яті, де знаходиться елемент стека, записаний останнім, називається вершиною стека. Адреса вершини, відлічений з початку сегмента стека, зобов’язаний перебувати у покажчику стека — регістрі SP. Отже, абсолютный адресу вершини стека визначається парою SS: SP. ——- ——- ——- SS: SP | | SS: SP | | SS: SP | | | ——- запис | ——- читання | ——- | | | =======> ——>| b | =======> | | | | ——- в стік ——- з стека | ——- ——->| a | | a | ——>| a | ——- ——- ——- Значення 0 в регістрі SP свідчить у тому, що стік повністю заповнений (його вершина «дійшла «на початок області стека). Тож контролю над переповненням стека треба перед нової записом в стік проверять умова SP=0 (сам ПК цього робить). Для порожнього стека значення SP має рівнятися розміру стека, тобто. пара SS: SP повинна вказувати на байт, наступний за останнім байтом області стека. Контроль за читанням з порожнього стека, коли треба, зобов’язана робити сама програма. Початкова установка регістрів SS і SP то, можливо зроблена в самій програмі, однак у MASM передбачена можливість автоматичної завантаження цих регістрів. Якщо директиві SEGMENT, початкуючою опис сегмента стека, вказати параметр STACK, тоді асемблер (точніше, завантажник) до того, як передати управління на першу команду машинної програми, завантажить в регістри SS і SP потрібні значення. Наприклад, тоді як програмі сегмент стека описаний так: ST SEGMENT STACK DB 256 DUP (?) ;розмір стека — 256 байтів ST ENDS і коли під цей сегмент було виділено область пам’яті починаючи з абсолютного адреси 12340h, тоді до початку виконання програми в регістрі SS виявиться величина 1234h, а регістрі SP — величина 100h (=256). Зазначимо, що це значення відповідають порожньому стеку. 1.7.2 Основні стековые команди За дотримання зазначених вимог щодо програми використовувати команди, призначені до роботи зі стеком. Основні з них являются такі. Запис слова в стік: PUSH op Тут op позначає будь-який 16-битовый регістр (зокрема і сегментный) чи адресу слова пам’яті. З цієї команді значення регістру SP зменшується на 2 (віднімання іде за рахунок модулю 216), після чого вказане операндом слово записується в cтек за адресою SS: SP. Читання слова з стека: POP op Слово, лічене з вершини стека, присвоюється операнду op (регистру, зокрема сегментному, але з CS, чи слову пам’яті), після чого значення SP поповнюється 2. Перехід з поверненням: CALL op У цю команду записує адресу наступній з ним команди у стік і далі робить перехід за адресою, визначеного операндом op. Її використовують для переходів на підпрограми з запам’ятовуванням в стеці адреси повернення. Є такі різновиду цієї команди (вони аналогічні вариантам команди безумовного переходу JMP): — внутрисегментный відносний довгий перехід (op — непосредственний операнд площею слово, а MASM — це мітка з поточного пєгмента команд чи ім'я близькій процедури (див. нижче)); у разі в стік заноситься лише поточне значення лічильника команд IP, тобто. смещение наступній команди; - внутрисегментный абсолютний непрямий перехід (op — адресу слова пам’яті, у якій перебуває адресу (усунення) тієї команди, яку і буде зроблено перехід); й тут в стік записується лише усунення пеклореса повернення; - межсегментный абсолютний прямий перехід (op — безпосередній операнд виду seg: ofs, а MASM — це FAR PTR чи ім'я дальньої процедури (див. нижче)); тут у стік заноситься поточні значення регистрів CS і IP (котра першою стік записується вміст CS), тобто. абсолютный адресу повернення, після чого змінюються регістри CS і IP; - межсегментный абсолютний непрямий перехід (op — адресу подвійного слова, де знаходиться пара seg: ofs, задающая абсолютний адресу перехода); й тут в стеці рятується вміст регістрів CS і IP. Перехід (повернення) за адресою з стека: RET op З стека зчитується адреса київська і у ній виробляється перехід. Якщо зазначений операнд (але це має бути ненегативне число), то після прочитання пеклореса стік ще очищається цього число байтів (до SP додається це число). Команда використовується для повернення з підпрограми за адресою, зай писаний в стік за командою CALL при виклик підпрограми, і одновременной очищення стека від параметрів, які основна програма занесла в стік перед зверненням до подпрограмме. Команда RET має чи два різновиди (хоча у MASM вони записуються і однаково): щодо одного випадку із стека зчитується лише одна слово — усунення адреси повернення, тоді як у другому — з стека зчитується пара seg: ofs, яка вказує абсолютний адресу повернення. Як асемблер визначає, який із цих двох випадків має місце, пояснено нижче. У ПК стік переважно використовується в організацію підпрограм і переривань. Підпрограми розглядаються нижче, а переривання — у розділі 3. Проте, навіть якщо програмі непотрібен стік, вона однак повинна відвести під нього місце. Річ у тім, що стеком буде неявно користуватися операційна система при обробці переривань, які виникають (например, при натисканні клавіш на клавіатурі) тоді, коли виконується програма. Для потреб ОС рекомендується виділяти в стеці 64 байта. 1.7.3 Підпрограми Типова схема огранизации підпрограм, зазвичай використовувана трансляторами з мов високого рівня для реалізації процедур і державних функцій (зокрема, рекурсивних), наступна. При зверненні до подпрограмме в стік заносяться параметри нею та «адреса возрата, після чого робиться перехід їхньому початок: PUSH param1 ;запис 1-го параметра в стік … PUSH paramk ;запис останнього (k-го) параметра в стік CALL subr ;перехід у возратом на підпрограму (Зауваження: якщо потрібно обчислити параметр або якщо її розмір відличен від слова, для записи параметра в стік потрібно, звісно, нісколько команд, а чи не одна.) Стан стека після виконання цих доманд звернення до подпрограмме показано на рис. a Першими командами підпрограми звичайно є такі: PUSH BP ;врятувати в стеці старе значення BP MOV SP, BP ;встановити BP на її вершину стека SUB SP, m ;відвести в стеці місце (m байтів) під локальні ;величини підпрограми (стан стека у цей ;момент показано на рис. б) Пояснимо ці «вхідні «команди. У подпрограмме для звернення до осередків стека, зайнятих параметрами, використовується (як) регістр BP: тоді як BP занести адресу вершини стека, то тут для доступу до цих ячейкам варто використовувати адресні висловлювання виду i[BP] чи, що таке саме, [BP+i]. (Зазначимо, що застосовувати тут регистры-модификаторы BX, SI і DI не можна, т.к. формовані із них виконавчі адреси будуть сегментуватися за умовчанням по регістру DS, а тому випадку потрібно сегментування по SS.) Проте ця підпрограма то, можливо викликана з іншої, також використовує регістр BP, тому, перш, ніж установити BP на її вершину стека, треба врятувати в стеці старе значення цієї регістру, що робить перша з «вхідних «команд. Друга команда встановлює BP на її вершину стека. Якщо припустити, кожен параметр та «адреса повернення займають за словами пам’яті, тоді доступом до першому параметру забезпечується адресним вираженням [BP+4], до другого — виражением [BP+6] тощо. (див. рис. б). Подпрограмме може знадобитися місце на її локальних величин. Таке місце зазвичай відводиться в стеці (а рекурсивних підпрограм — лише у стеці) «над «осередком, займаній старим значенням BP. Якщо під ці величини потрібно m байтів, такий «захоплення «місця можна реализовать простим зменшенням значення регістру SP на m, що робить 3-тя «вхідні «команда. Доступ до локальних величинам забезпечується адресуными висловлюваннями виду [BP-i]. Якщо подпрограмме непотрібно місце під локальные величини, тоді третю з «вхідних «команд слід опустити. Вихід із підпрограми реалізується такими командами: MOV SP, BP ;очистити стік від локальних величин POP BP ;відновити старе значення BP RET 2*k ;повернення з підпрограми очищення стека від ;параметрів (вважаємо, що вони займають 2*k байтів) Перша з цих «вихідних «команд заносить в регістр SP адресу тієї осередки стека, де зберігається старе значення регістру BP, тобто. відбувається очистка стека від локальних величин (якщо їх було, то цю команду треба опустити). Друга команда відновлює в BP цією старою значение, одночасно видаляючи його з стека. Саме тоді стан стека буде так ж, як і для входом в підпрограму (див. рис а). Третя команда зчитує з стека адресу повернення (у результаті SP «опускається «на 2 байта), потім додає до SP число, які мають рарняться числу байтів, займаних усіма параметрами підпрограми, і позатим здійснює перехід за адресою повернення. Саме тоді стан стека буде так ж, яким було перед зверненням до подпрограмме. Тут описана універсальна схема роботи підпрограм. У кокретных ж випадках можна використовувати простіші схеми. Напризаходів, параметри можна передавати не через стік, а ще через регістри, місце під локальні величини можна відводити над стеці, а сегменті даних, і т.п. 1.7.4 Процедури у мові ассемблера Під час упорядкування і виклик підпрограм треба пильнувати те, щоб команди CALL і RET діяли узгоджено — були водночас близькими чи далекими. У MASM цю проблему знімається, якщо подпрограмму описати як процедуру. Процедури мають такий вигляд: имя_процедуры PROC [NEAR чи FAR] … имя_процедуры ENDP Хоча у директиві PROC після імені процедури не ставиться двокрапка, це належить до міткам і можна вказувати в командах переходу, зокрема у команді CALL, коли треба викликати процедуру. Це ж ім'я має бути точно повторений в директиві ENDP, заканчивающей опис процедурепи. Пропозиції між двома директивами утворюють тіло процедуры (підпрограму). Ім'я процедури є фактично міткою перша з команд тіла, тому цю команду зайве спеціально мітити. Якщо директиві PROC зазначений параметр NEAR чи не указан, така процедура вважається «близькій «і звертатися до неї тільки з того сегмента команд, де описана. Річ у тім, що ассемблер буде заміняти все команди CALL, де зазначено ім'я даної процедурепи, на машинні команди близького переходу з поверненням, проте команди RET всередині процедури — на близькі повернення. Якщо ж у директиві PROC зазначений параметр FAR, це «далека «процедура: всі мої звернення до неї і всі команди RET в ній розглядаються ассемблером як далекі переходы. Звертатися до цій процедурі можна з будь-яких сегментів команд. Отже, варто лише вказати тип процедури (близька вона чи далека), всю ж іншу роботу візьме він асемблер: переходи її у і повернення з її будуть автоматично узгоджені з цей тип. У цьому вся головне (і єдине) гідність описи підпрограм в віде процедур. (Зазначимо, що мітки і імена, достойні процедурі, не локалізуються у ній.).