Еволюція засобів проектування програм протягом останніх десятиліть здатна здивувати будь-яку людину, програмного забезпечення, що займається розробкою. Особливо це стосується написання програм для операційних систем сімейства Windows. Сучасні інструментальні засоби настільки розвинені, що розробник програмного забезпечення може отримати готову програму за допомогою декількох клацань. Величезна кількість книг, статей і початкових текстів програмної коди присвячено проектуванню програм на Pascal, Basic і інших мовах програмування. Мова низького рівня — мова асемблера — після смерті" MS-DOS, здавалося, доживає останні дні. Але всупереч прогнозам він не зійшов з арени і продовжує вельми широко використовуватися при розробці програм. У чому ж секрет живучості цієї мови? Відповідь досить проста і може бути сформульований однією пропозицією: мова асемблера — це мова, на якій "говорить" процесор, і зникнути він може тільки разом із зникненням процесорів! З цієї ж причини асемблер має одну фундаментальну перевагу перед мовами високого рівня: він найшвидший. Більшість додатків, що працюють в режимі реального часу, або написані на асемблері, або використовують в критичних ділянках коди асемблерні модулі. Нерідко можна чути твердження про те, що процес розробки програм на мові асемблера дуже трудомісткий і віднімає масу часу. Ще однією перешкодою для роботи з асемблером рахують його складність. І, нарешті, як аргумент приводиться твердження, що розробка додатків на асемблері сильно утруднена через відсутність сучасних інструментальних засобів для проектування і відладки. Такі твердження, взагалі-то, не відповідають дійсності. Мова асемблера не складніше за інші мови програмування і досить легко освоюється як досвідченими, так і початкуючими програмістами. Крім того, останніми роками з'явилися дуже могутні інструментальні засоби розробки програм на асемблері, і це вимушує по-іншому поглянути на процес розробки програм на цій мові. Серед таких інструментальних засобів проектування можна назвати макроасемблер
Можна повністю покластися на компілятор ("все зроблено до нас"), який згенерує для вас оптимальний код, і взагалі не займатися поліпшенням якості програми. При цьому у цілому ряді випадків може і не знадобитися ніяких доопрацювань і поліпшень. Наприклад, при створенні невеликих офісних застосувань або утиліт тестування мережі оптимізація зазвичай не потрібна. Проте в більшості випадків обійтися без ручної оптимізації і покладатися тільки на стандартні можливості компіляторів не можна. З проблемою поліпшення продуктивності, хочете ви цього чи ні, вам неминуче доведеться зіткнутися при розробці більш менш серйозних застосувань, наприклад баз даних, будь-яких клієнт-серверних або мережевих застосувань, причому оптимізуючий компілятор того середовища, в якому ви працюєте, в більшості випадків значного виграшу вам не забезпечить. Якщо програміст розробляє програми, що працюють в реальному часі, такі як драйвери пристроїв, системні служби або промислові застосування, то без дуже серйозної роботи по ручному доведенню коди до оптимальної продуктивності завдання написання програми просто не буде виконано. І справа тут не в тому, що засоби розробки недосконалі і не забезпечують того рівня оптимізації, якою від них вимагається. Будь-яка більш менш серйозна програма має стільки взаємозв'язаних параметрів, що жоден засіб розробки не поліпшить її так, як це може зробити сам програміст. Процес оптимізації програм схожий швидше мистецтву, чим "чистому" програмуванню, і важко піддається алгоритмізації. Поліпшення продуктивності програм — процес зазвичай трудомісткий, такий, що займає значну частину часу. Хочеться відзначити, що не існує єдиного критерію оптимізації. Більш того, сам процес оптимізації досить суперечливий. Наприклад, якщо добитися зменшення об'єму пам'яті використовуваного програмою, то за це доведеться розплатитися втратою швидкодії роботи програми. Жодна програма не може бути одночасне надшвидкою, зверхмалою за розміром і повнофункціональною для користувача. До цього можна скільки завгодно наближатися, але ідеального застосування вам отримати ніколи не вдасться. Хороші програми зазвичай поєднують ті або інші якості в розумних пропорціях, залежно від того, що важливіше: швидкість виконання, розмір програми (як файлу додатку, так і об'єму пам'яті, займаного працюючим застосуванням) або зручність інтерфейсу користувача. Для багатьох офісних застосувань дуже важливим показником є зручність інтерфейсу користувача і якомога вища функціональність. Наприклад, для користувача програми електронного телефонного довідника той факт, що програма працює на 10% швидше або повільніше, особливого значення не має. Розмір такої програми, в принципі, не критичний і також не має особливого значення, оскільки об'єм сучасних жорстких дисків досить велика, щоб помістити десятки і навіть сотні таких електронних довідників. Профамме може бути необхідне десятки мегабайт оперативної пам'яті для роботи — це теж зараз не проблема. Але можливість зручної маніпуляції даними для користувача буде дуже важливою. Якщо додаток використовує клієнт-серверну модель обробки даних і взаємодії з користувачем, як, наприклад, більшість мережевих застосувань, то критерії оптимізації тут будуть декілька іншими. На перше місце можуть вийти проблеми використання пам'яті (особливо для серверної частини додатку) і оптимізації мережевої взаємодії з клієнтською частиною. Додатки, що працюють в режимі реального часу, критичні по синхронізації отримання, обробки і, можливо, передачі даних за прийнятні інтервали часу. Подібні програми вимагають, як правило, оптимізації по завантаженню процесора і синхронізації з системними службами операційної системи. Якщо ви — системний програміст і розробляєте драйвери або сервіси для роботи з операційною системою, наприклад з Windows SP - 3, то неефективний програмний код в кращому разі тільки уповільнить роботу всієї операційної системи, а про гірші наслідки можна тільки здогадуватися. Як видимий, підвищення продуктивності програм залежить від багатьох чинників і у кожному конкретному випадку визначається тим, що ця програма повинна робити. Розглянемо тепер детальніше, як можна виконати оптимізацію програм, і проведемо невеликий порівняльний аналіз різних методів підвищення продуктивності виконання додатків. простий спосіб змусити додатки працювати швидше — це підвищити обчислювальну потужність комп'ютера за рахунок установки продуктивнішого процесора або збільшення об'єму пам'яті, тобто зробити апгрейд апаратної частини. В цьому випадку проблема продуктивності буде вирішена сама собою. Якщо ви прихильник такого підходу, то найшвидше попадете в безвихідь, оскільки весь час залежатимете від апаратних рішень. Доречно зауважити, багато очікувань щодо продуктивності нових поколінь процесорів, нових типів пам'яті і архітектури системних шин виявляються явно перебільшеними. Їх продуктивність на практиці виявляється нижче задекларованою фірмами-виготовлювачами. Так, наприклад, нові мікросхеми пам'яті, як правило, перевершують своїх попередників даних, що за об'ємом зберігаються, але зовсім не по швидкодії. Продуктивність жорстких дисків також росте повільніше, ніж їх об'єм. Якщо ви розробляєте комерційне застосування, то повинні враховувати, що у користувача може не бути найостанніших моделей процесора і швидкодіючої пам'яті. До того ж, далеко не всі користувачі горять бажанням викласти гроші на новий комп'ютер, якщо їх цілком влаштовує те, що у них вже є. Тому навряд чи варто вважатися серйозно на рішення проблем з програмним забезпеченням за допомогою однієї тільки закупівлі нового устаткування. Програмування на мові Асемблера Чому вас могло б зацікавити програмування на мові асемблера? Можливо, ви вже знайомі з однією мовою високого рівня. Якщо ви постійно користуїтесь персональним комп'ютером , то ви знаєте, що інтерпрітатор Бейсика є частиною системи. Навіщо ж возитисяще з однією мовою програмування, тим більше з таким, який обіцяє певні труднощі? Очевидно, навіть розташовуючи сучасними могутніми мовами, ви все ще потребуєте асемблера із-за його ефективності і точності. Асемблерні програми можуть бути дуже ефективними. З програмістів, з рівними навиками і здібностями, що працює на мові асемблера створить програму компактнішу і бистродіючу, чим така ж програма, написана на мові високого рівня. Це так практично для всіх невеликих або середніх програм. На жаль, у міру зростання розмірів, програми на мові асемблера втрачають частину своїх переваг. Це відбувається із-за необхідної в асемблерній програмі уваги по деталям. Як ви побачите, мова асемблера вимагає від вас планування кожної дії комп'ютера. У невеликих програмах це дозволяє оптимізувати роботу програми з апаратними засобами. У великих же програмах величезна кількість деталей може перешкодити вам еффективно працювати над самою програмою, навіть якщо окремі компоненти програми виявляться дуже непоганими. Безумовно, програмування на мові асемблера відповідає потребам не кожної програми. Програми на мові асемблера дуже точні. Оскільки ця мова дозволяє програмістові безпосередньо працювати зі всім апаратним забезпеченням, асемблерна програма може робити те, що недоступно ніякий іншій програмі . Поза сумнівом, що в програмуванні пристроїв введення-виводу, де потрібний контроль над окремими розрядами регістрів пристроїв, програмування на мові асемблера - єдиний відповідний вибір. Ясно, що ефективність і точність мови асемблера дають певні переваги. Але його деталізованість створює і деякі проблеми. Чи ж варто обирати для програмування мову асемблера? Звичайно ви повинні користуватися програмами на мові асемблера, коли немає іншого способу написати програму. Наприклад програмісти фірми Майкрософт писали з використанням процедур асемблера всі програми управління пристроями введення-виводу для РС. Для управління пристроями введення-виводу і системою переривань було потрібно ту точність мови асемблера яку не може забезпечити жоден інша мова програмування. Аналогічно, на мові асемблера у фірмі Майкрософт писалися процедури діагностики які повинні перевіряти кожну деталь апаратури. Мова асемблера необхідна також і в тих випадках, коли головними є робочі характеристики програми. Це може бути час виконань або кінцевого розміру програми. Яка програма не підходить для мови асемблера? Звичайно, ви можете написати на нім будь-яку програму, проте з великою програмою краще працювати в мові високого рівня, такому як Бейсик або Паскаль. Ці мови дозволяють вам зосередитися на своїй проблемі. Вам не доводиться безпосередньо мати справи з тонкощами апаратного устаткування і процесора. Мови високого рівня дозволяють вам відступити назад і за деревами побачити ліс. Очевидно далі, що ви потребуєте з'єднання програм мови асемблера з програмами мов високого рівня. Тут ми обмежимось програмуванням на мові асемблера для тих завдань, для яких він добре підходить, таких як управління введенням-виводом. Також розглянемо проблемі з'єднання програм на мові асемблера з другмі мовами програмування. Ці методи надають вам все краще, що є в обох світах. Ви можете, коли необхідні точність і ефективність, використовувати процедури на мові асемблера - і процедури високого рівня для програми в цілому. Все що ви для цього повинні зробити - зчепити їх разом. І остання причина для вивчення програмування на мові асемблера. Тільки через написання програм на цьому рівні деталізації ви можете зрозуміти як працює машина на самому ніжнем рівні. Якщо ви хочете дізнатися про комп'ютер все, ви повинні бути знайомі з його мовою асемблера. Єдиний спосіб добитися цього писати програми на цій мові.
Основи комп'ютерних обчислень
У цьому розділі роз'яснюються властивості комп'ютерів. Вона розповість вам як комп'ютери працюють і чому вони роблять це саме так. Деякі положення можуть виявитися знайомими вам. Якщо у вас немає досвіду програмування на мові асемблера, то багато операцій будуть для вас новими.
Двійкова арифметика
Всі комп'ютери використовують для зберігання інформації двійкову систему. Це означає, що кожен елемент інформації, що зберігається, може мати тільки два стани. Ці стани позначаються як "включений" і "вимкнений", "істина" і "брехня", або "1" і "0". Комп'ютер зберігає ці значення у вигляді рівнів напруги. На щастя у нас немає потреби звязуватись з напругою. При написанні програм ми маємо справу тільки з числами. Використовуючи прості числа 0 і 1, можна виконувати дуже складні обчислення. Із-за двійкового уявлення даних комп'ютери використовують в своїх обчисленнях арифметику з двійковою підставою. Арифметика з підставою 2 користується тільки двома цифрами: 0 і 1. Ми зазвичай застосовуємо систему числення по підставі 10. У десятковій арифметиці уживається десять різних цифр - від 0 до 9. Двійкову арифметику можна представити собі як систему для людей, що мають тільки два пальці. Обмеження лише десятьма цифрами в десятковій арифметиці не заважає нам уявити крупніші числа. Ми користуємося багатозначними числами, в кожній позиції яких присутні різні ступені 10. Найправіша цифра будь-якого числа позначає число одениць, сусідня зліва - кількість десятків, наступна, - число сотень і так далі Прогресія справа наліво шикується така:
10**0, 10**1, 10**2 і так далі Число 2368 в дійсності
представляє 2 тисячі, 3 сотні, 6 десятків і 8 одениць. Фіг. 2.1 показує, кажучи математичною мовою, розкладання числа 2368.
Арифметика з підставою 2 або двійкова система аналогічна десятиричній, за винятком того, що розряди числа тут відповідають
ступеням 2 а не 10. Числа більше 1 представляються багатозначними числами, так само як в десятковій арифметиці багатозначне уявлення отримують числа більше 9. Кожна цифра в двійковій системі називається біт від Binary digit (двійкова цифра). Позиція кожного біта в числі відповідає деякій мірі 2. Фіг. 2.2 показує значення двійкового числа 101101b.
Ми користуватимемося суфіксом "B" для позначення чисел в двійковому уявленні. Цим вони відрізнятимуться від десяткових що не мають суфікса. Наприклад, 2368 - це десяткове число, а 101101b - двійкове. У математичній літературі для позначення системи числення зазвичай використовується індекс. Ми будемо користуватися символом "B", оскільки асемблер ПК для позначення двійкових чисел застосовує саме його.
У таблиці Фіг. 2.3 для представлення максимального десяткового числа потрібно 4 біта. Для крупніших чисел буде потрібно ще більше біт. Двійковим числом складається з n біт можна зобразити число величиною 2**n-1.
Десяткове Двійкове
-----------------------
1 1
2 10
3 11
4 100
5 101
6 110
7 111
8 1000
9 1001
10 1010
-----------------------
Фіг. 2.3 Перших 10 цілих єдиним чином представити будь-яке ціле від 0 до 2**n-1. Для 4-х бітового прикладу на Фіг 2.3 найбільше таке число рівне 15 (2**4-1). Для кожного конкретного мікропроцесора існує максмальний розмір двійкових чисел, які можуть бути в нім представлені.
Шістнадцяткове уявлення
Двійкова арифметика хороша для комп'ютера, оскільки він має справу тільки з оиницями та нулями. Але людське сприйняття вимагає компактнішого уявлення. Ми користуватимемося шестнадцятерічним уявленням даніх для власної зручності. Шістнадцятиричне уявлення чисел - це система числення по підставі 16. Кожна цифра в числі може мати значення від 0 до 15. Кожен розряд в числі є ступенем 16. Шістнадцятиричне уявлення - зручний метод запису двійкової інформації. Кожна шістнадцятирична цифра відповідає чотирьом бітам. Для претворення двійкового числа в шістнадцятиричне розбийте його на групи по 4 біта і прочитайте кожну групу як шістнадцятиричну цифру. Це дає ущільнення запису один до чотирьох - дуже зручно для розумної істоти. Виника невелика трудність з тим, що у нас є цифри тільки від 0 до 9. Числа від 10 до 15 ми представлятимемо першими шістьма буквами латинського алфавіту: від A до F. Таблиця відповідності між десятковими, шістнадцятиричними і двійковими цифрами приводиться на Фіг. 2.7.
Як показано в цій таблиці, кожна шістнадцятирична цифра відповідає точно 4-м бітам якого-небудь двійкового числа. Шістнадцятиричне уявлення звичайне для машин, в яких розмір слова кратний 4. Оскільки слово в 8088 складає 16 біт, ми будем користуваться шістнадцятиричним записом. Кожне 16-бітове значення представляєтся чотирма шістнадцятиричними цифрами. числа в Надалі числа у шістнадцятиричному записі позначатимуться
Десяткові числа пишуться без суфікса або з суфіксом "D". Це в точності відповідає записи чисел в мові асемблера. Для уявлення даніх в асемблерні програмі можна використовувати любу з трьох розглянутих систем (десяткова, двійкова і шістнадцатеричну).
При записі шістнадцятиричних чисел важливо переконатися, що ассемблер сприйме їх як числа. Якщо ви ввели "FAH", то це може бути або шістнадцятиричне число FA, або ім'я змінної FAH. Асемблер припускає, що число починається з цифри і що мітка починається з букви. Тому "FAH" для асемблера виявляється змінною. Якщо ми маємо на увазі не змінну а число, то його треба записати як "0fah": це число має бажане значення і починається свідомо з цифри. Щоб не виникло плутанини кожному шістнадцятиричному числу яке починається із значень від A до F повинен передувати 0.
Вивчав і займався Асемблером в студентські роки і декілька років після навчання. Потім закинув за непотрібністю в школі. Після всього прочитаного вище напевно знову почну займатися. Не обовязково повністю складати програми на Асемблері, а можна тільки окремі фрагменти і вставляти їх в Паскаль, або Дельфі. І збережеться візуальний інтерфейс програми і швидкість виконання збільшиться.
Програмування на мові асемблера вважається складною задачею, причини цього такі: 1. Мова асемблера будь-якого процесора суттєво складніша будь-якої мови високого рівня. Щоб скористатись всіма можливостями мови асемблера, треба по крайній мірі знати команди мікропроцесора, а їх число з усіма можливими варіантами переважає 100, їх кількість значно перевищує кількість операторів і ключових слів інших мов високого рівня. Проблема ускладнюється ще тим, що зміни в асемблері виникають набагато швидше ніж в мовах високого рівня, це зв’язано з появою нових мікропроцесорів і відповідно нових команд. 2. Програміст, який використовує мови асемблера повинен сам слідкувати за розподілом пам’яті та вмістом регістрів, щоб коректно розподіляти і оперувати пам’яттю, в мовах високого рівня це робиться автоматично при допомозі компілятора, але ця обставина має перевагу: можна оптимально розташувати дані в пам’яті, забезпечити максимальну швидкість виконання та мінімальну довжину програми. 3. Програми на мові асемблера важче проектувати та підлагоджувати, треба весь час пам’ятати, що конкретно знаходиться в кожному регістрів в даній комірці пам’яті. Прийнято вважати, що розробка програми тільки на мові асемблера, деякого процесора, навіть якщо він поширений не рекомендується. Зрозуміло, що будь-яку програму можна написати тільки з допомогою асемблера, але для цього треба використати набагато більшу кількість команд і час який піде на її виконання і відладку буде набагато більший ніж для мови високого рівня. Набагато вигідніше писати програми на мові високого рівня, а найбільш критичні частини на швидкодії писати на мові асемблера. Наприклад на асемблері можна скласти процедури для реалізації вводу-виводу низького рівня, процедури обробки переривань та деякі інші.
Етапи створення програми. Розробка програми на мові асемблера включає кілька етапів. 1) Підготовка початкового тексту програми; 2) Асемблювання програми(отримання об’єктного коду); 3) Компоновка програми(отримання виконуваного файлу); 4) Відладка програми(знаходження помилок). Ці етапи циклічно повторюються.
Підготовка лістінгу програми
Текст програми на мові асемблера записується в один або кілька файлів, імена файлів і їх розширення можуть бути будь-які, прийнято для файлів які містять програми мовою асемблера прийнято використовувати розширення *.asm. Для файлів визначених констант і повних типів розширення *.inc. Ці файли є текстовими їх можна набрати в будь-якому текстовому редакторі. Можна також використати інтегровані середовища для розробки програм, при програмуванні зручно виділяти один каталог для збереження всіх файлів програми і звідти запускати всі необхідні програми для підготовки, асемблювання та компонування програми. При використанні стандартних редакторів тексту, редаговані тексти треба зберігати у вигляді звичайних файлів у форматі ASCII, це означає, що ці файли треба зберігати без додаткових символів, ці символи часто вставляють в текст різні спеціалізовані редактори, наприклад WORD
Асемблювання програми.
Підготовлений текст мови асемблер є початковим для спеціальних програм, які називаються асемблерами, далі програма асемблера. Задача програми асемблера перетворити текст програми у форму двійкових команд, останні можуть вже бути виконанні мікропроцесором. Після асемблювання дістають так звані файли об’єктних модулів, які мають відповідні розширення *.obj. Для отримання об’єктних файлів необхідного виконати відповідну програму асемблера masm фірми Microsoft, або tasm фірми Borland. В обох випадках після команди вказується ім’я файлу : masm prog1.asm, tasm prog1.asm. Така форма команди є мінімальною, крім цієї форми можна використовувати іншу форму задаючи перед іменем файлу опції або ключі. Якщо програма складається з декількох файлів, то їх асемблювання проводиться незалежно один від одного, хоча отримані об’єктні файли представлені вже в двійковій формі запускати їх на виконання не можливо
Компоновка програми
Текст програми може знаходитись в декількох файлах, змінні які описані в цих файлах можуть використовуватися спільно, якщо такі файли асемблюються окремо, то не можливо дістати повну інформацію, для того, щоб генерувати виконавчий код. Тому процес підготовки програми обов’язково включає в себе етап компоновки. На цьому етапі визначають всі невідомі, при окремому асемблювані, адреси всіх змінних або функцій, які використовуються спільно. Процес об’єднання об’єктних модулів в один файл виконується спец. програмою, яка називається компоновщиком. Це може бути програма link фірми Microsoft, або tlink фірми Borland, отримуваний виконуваний файл має розширення *.exe, або *.com. Компановщику треба передати імена відповідних об’єктних файлів. Link prog1.obj prog2.obj Tlink prog1.obj prog2.obj
Після компонування отримується виконуваний файл, він отримує ім’я файла, який стоїть перший у відповідній команді.Відладка програми. За винятком початкових простих програм практично всі програми на мові асемблера мають потребу в відладці. Для відладки можна використовувати різні відладчики, наприклад tg386-Turbo Debuger фірми Borland. Сучасні відладчики дозволяють в процесі відладки контролювати значення регістрів загального призначення, а також значення змінних і змінювати їх в процесі відладки, можна переглядати зміст різних ділянок пам’яті, можна контролювати виконання покроково, або розставляти точки зупинки.Використання інтегрованих середовищ (ІС). Дуже зручно користуватись для підготовки тексту програми редакторами ІС, такі можливості передбачають практично всі виробники сучасних асемблерів. ІС мають суттєву перевагу, так як дають доступ до довідкової інформації. Вони дозволяють також зразу асемблювати та компонувати набрані тексти і провести його відладку. Потім знову можна повернутись до його редагування, при цьому складається ілюзія роботи з однією програмою. Слід відмітити, що відладчики вбудовані в ІС мають дещо обмежені можливості
Основні відомості та правила для написання програм на асемблері
Всі данні в мікропроцесорі представляються у вигляді набору бітів певної довжини, тільки окремі команди мікропроцесора розглядають вміст регістру або комірки пам’яті в певному форматі. В переважній більшості випадків сам програміст вирішує як розглядати певний набір бітів: як код клавіш, як число без знаку чи зі знаком, або як двійково-десяткове число. Всі числа які представляють певні данні розглядаються в позиційній системі числення. При представленні чисел команді мікропроцесора використовується двійкова система числення. Щоб спростити процес програмування мова асемблера дає можливість данні записувати так, як це зручно програмісту, при цьому можна записувати данні в різних системах числення. Можна навіть замість числа записувати вирази, процес перетворення цих чисел у двійковий вигляд буде виконувати програма асемблера. При записі двійкових чисел використовують суфікс В або в: 11011111В, 11010101в. При записі чисел в 16-вій системі числення треба враховувати, що для чисел 10-15ами. За винятком пбукви латинського алфавіту. Щоб відрізнити 16-ві числа дописується суфікс Н або h, крім того перш0пбуква в записі шістнадцяткового числа повинн0пбути арабською цифрою. Тобто для написання числа c5h не достатньо суфікса h, треба обов’язково поставити спершу 0:0C5h. За умовчанням використовується десяткове CЧ. При допомозі директиви radix можна міняти CЧ.Двійкові числа.Для великої кількості команд допускається, що інформація представлена в двійкових кодах, довжина цих двійкових чисел може бути різною. Мікропроцесори сімейства І 80х86 підтримують дані таких розмірностей: 1. Байти; 2. Слова; 3. Подвійні слова; 4. Рядки. Крім того до специфічних даних можна віднести адрес переходів. Байт – це мінімальна к-сть інформації при роботі мікропроцесора, складається із 8 біт. Вся пам’ять мікропроцесора може розглядатись як велика к-сть байтів. 210б=1Кб 220б=1Мб 230б=1Гб 240б=1Тб
Так як байт складається з 8 біт, кожен біт може перебувати в двох станах, то байт може перебувати в 256-тьох станах. Крім представлення чисел байт використовується для збереження символьних кодів, наприклад це може бути символ, що набирається з клавіатури. 256 різних значень байта дозволяють описати з допомогою ASCII не тільки десяткові числа, а й весь англійський алфавіт, а також знаки пунктуації, спеціальні знаки, управляючі символи, символи національних алфавітів. Слово може містити 216=65536 різних станів, а це є 64 Кб. Слово це найбільш зручний спосіб збереження інформації в даному сімействі мікропроцесорів, адже їх внутрішні регістри як мінімум 16-ти розрядні. У всіх мікропроцесорів фірми Intel правило запису багатобайтних чисел таке: молодший байт знаходиться за молодшою адресою в пам’яті, старший за старшою адресою. Адреса деякої ділянки пам’яті яка розглядається як слова рівна адресі молодшого байту. Наприклад, якщо за адресою N записано слово 1539H, то це означає, що за адресою N записаний байт 39H, а за адресою N+1 – 15Н. Важливим є число 65536, для процесорів 80х86 це число визначає максимальну довжину сегменту, тобто області пам’яті, яка містить код або данні програми, тому за допомогою таких чисел до 65536 можна адресувати комірки пам’яті всередині даного сегменту, така адресація, коли використовується зміщення всередині сегменту називається ближньою – Near. Подвійні слова рівні 4 байт, або 32 біт. Одне слово може мати 4294967296=4Мб. Подвійні слова зберігаються в пам’яті за цим же правилом: молодший байт за молодшою адресою, байти в пам’яті розташинятком ппослідовно один за одним 12554959Н – слово подвійне, якщо воно знаходиться за адресою N, то за адресою N знаходиться байт 59Н, N+1 – 49H, N+2 – 55H, N+3 – 12H. Подвійне слово при програмуванні для процесорів молодших 80386 має достатньо місця для збереження не тільки адреси всередині сегменту, а й адреси початку сегменту і адреси зміщення в середині сегменту називається дальньою – for adress адресою. При програмуванні , коли для завдання адреси використовується 32 біти (для мікропроцесорів 80386 і старше) в подвійному слові кожна зберігати 32-розрядне зміщення. За допомогою цього зміщення можна звертатись до будь-якої комірки пам’яті. Рядки в мікропроцесорах родини І 80х86 можна виконувати операції над рядками. Рядок – це послідовність байтів або слів, а для мікропроцесорів старших 80386 подвійних слів, які знаходяться в пам’яті комп’ткера. Мінімальна довжина рядку 1 елемент, максимальна довжина рядку може досягати 64 Кб для мікропроцесорів молодших 80386, і 4Гб для мікропроцесорів старших 80386.
Представлення чисел зі знаком
Мікропроцесори даної родини підтримають обробку чисел зі знаком, для чисел, які мають розмірність байт, слово, подвійне слово. Для представлення знаку використовують самий старший розряд числа 0 – “+”, 1 – “-“. В зв’язку з цим діапазон представлення чисел звужується. Наприклад для байтів зі знаком мінім. число : -128, макс. число : 127. Відповідно слово зі знаком : -32768 і 32768, подвійне слово : -2147483648 і 2147483647. Від’ємні числа зберігаються в додатковому коді. Використання двійквого коду має переваги: можна виконувати арифметичні операції не знаючи як представлені операнди: зі знаком чи без знаку. Це означає, що сам програміст вирішує як інтерпретувати результат зі знаком чи без. Двійково-десяткові числа. Мікропроцесори даної родини дозволяють працювати з так званими двійково-десятковими числами, вони представляють собою числа від 0 до 9 записані в двійковому вигляді, для їх запису використовується 4 біти. На відміну від просто двійкових чисел тут не ми. За винятком птетради, які відповідають числам a – f 16-вої СЧ. Мікропроцесори цьої родини підтримують роботу з двома форматами двійково-десяткових чисел: унакованими і неунакованими двійково-десятковими числами. Приклад структури програми на мові асемблера (Шаблон) Приведемо основні директиви, які використовуються і програмі на мові асемблера. Тепер використовуються спрощені способи опису сегментів. Сегменти – це частини програми (сегменти даних). Текст програми можна набирати великими або малими буквами, програма асемблера самостійно переводить текст у верхній регістр. Обов’язково у тексті програми писати коментарі. Текст який стоїть за символом “;” є коментарем, він ігнорується при асемблюванні програми. Директива яка як правило йде з початку програми dosseg описує розташування сегментів. Директива mode small – директива опису моделі пам’яті, наприклад в цьому випадку говорять, що модель пам’яті типу small, тобто є один сегмент коду і один сегмент коду і один сегмент даних. Директива .stack 100 – директива, яка задає початок сегменту стеку. В стеці зберігаються тимчасові змінні і адреси повернення з підпрограми або переривань. Як правило будь-яка програма має сегмент стеку. В даному випадку такий запис директиви .stack 100 позначає, що стек має довжину 100байтів. Точну величину стеку визначає число викликів підпрограм та системних ф-цій, стек треба збільшувати. В кінцевому підсумку розмір стеку підбирають експериментально, коли програма вже написана і відлагоджена. Директива .data – директива початку сегменту ініціалізованих даних, після цього рядка поміщають змінні значення яких відоме (ініціалізовані змінні). Директива .data ? – директива початку сегменту неініціалізованих даних. Директива .code – початок сегменту коду. Сегмент коду – це частина коду яка містить команди МП, власне це й є сама програма. В деяких випадках в цей сегмент можна поміщати не тільки команди, а й дані. Наприклад у підпрограмах обробки переривань. Директива .startup – точка початку виконання програми. З наступного після даного рядка починається виконання програми після її завантаження в пам’ять, ця директива повинн0пбути обов’язково всередині сегмента коду, але не обов’язково н0пйого початку. Директива .exit 0. – це директива закінчення програми при виконанні цього рядка управління буде передане ОС. Таких директив в програмі може бути кілька, число після директиви може приймати кілька значень, це число після закінчення програми присвоюється змінній ОС з назвою ERRORLEVEL, ця змінна використовується в командних файлах. Є спеціальні команди для роботи з цією змінною. Використовуючи різні числа можна повідомляти ОС, яка причина закінчення програми. Директиви початку і закінчення програми є по суті макровизначеними, вони визначають необхідний код який автоматично додається на початок і кінець програми. Цей код можна подивитись у файлі лістингу після асемблювання. End – директива закінчення тексту програми на мові асемблера. Рядки які розміщені після директиви end можна писати коментар в довільній формі.
Мінімальна одиниця тексту програми на мові асемблера – це рядок. Асемблер проглядає програму порядкові зверху в низ, кожний рядок закінчується символом переведення рядка Vпйого довжина в більшості випадків не може перевищувати 255 символів, однак зручно обмежувати довжину рядка широкою екрану (80 символів). При необхідності вводити в програмі довгі рядки їх можна об’єднувати, в кінець рядка при цьому потрібно включати символ \. Рядок 1 \ Рядок 2 Рядок 1 Рядок 2. В текст програми можна вставляти порожні рядки, які складаються тільки із розділювачів. В мові асемблера розділювачами служать символи пропуску та горизонтальна табуляція. При допомозі пустих рядків зручно розділяти логічні частини програми. Рядок може складатись тільки із коментарів, в такому випадку перший символ рядка обов’язково ;. В кожному рядку може розміщуватись або команда мікропроцесора, яка записана у мнемонічному (словом) вигляді, або директива, директиви інакше називають псевдооператорами. Команда мікропроцесора – це інструкція яку мікропроцесор буде виконувати в процесі виконання програми. Асемблер переводить рядки з мнемоніками (буквеними записами команд) у послідовності байт, які безпосередньо може виконувати процесор. Директиви служать для визначення даних, які використовуються в програмі і для управління процесом асемблювання. Рядок у програмі на мові асемблера може складатися з 4 полів: 1. Поле мітки (поле імені); 2. Поле оператора (або псевдооператора); 3. Поле операндів; 4. Поле коментарів. Обов’язковим є тільки поле оператора або псевдооператора, всі решту полів можуть бути відсутні. Мітка є спрощеною мнемонічною формою запису адреси команди в сегменті коду. Так саме ім’я є спрощеною формою запису вмісту деякої переважно в сегменті даних. Отже рядки в програмі записуються в такому загальному вигляді: [< мітка >]: < команда > < операнди >; коментарі < ім’я > <директива < операнди >; коментарі Потрібно звернути увагу на відсутність “:” після імені. Поля в рядках мови асемблера розділяються між собою на крайній мірі одним пропуском. Розглянемо поля детальніше. Поле мітки. При визначенні в програмі мітки або імені змінної можна використовувати букви латинського алфавіту великі або малі, а також символи _ @ $. Максимальна к-сть символів з яких може складатись мітка або ім’я 255, але відрізняються мітки та імена по перших 32символах. Доцільно в програмі використовувати осмислені імена. Зручно і програмі для швидкого розпізнавання міток від імені починати всі мітки з букви L і записувати їх тільки великими буквами. При записі імен змінних зручно використовувати префікс, який означає тип змінної, наприклад : skp_byte. При програмуванні з використанням асемблера masm, або tasm можна використовувати будь-яке число локальних міток @@. Для переходу до таких міток треба використовувати зарезервовані слова: @F – forward; @B – backward. Перш0п@F – це перехід до наступної мітки, @B – це перехід до попередньої мітки. Мітка або ім’я змінної не повиннVпбути зарезервованим словом.
Поле операндів Поле операндів залежить від попереднього поля
Поле коментарів. Структура програми на мові asm.
При роботі будь-якої програми в будь-який момент часу можна виділяти декілька ділянок пам’яті з якими працює процесор. Використання сегментів є особливістю процесорів х86. Воно переслідує таку мету:ами. За вання сегментів є спробою захаватити незв’язані ділянки пам’яті в програмі. Допускалось, що для того, щоб визначити адресу деякої комірки пам’яті потрібно знати дві складові цієї адреси: адресу початкового сегменту та адресу зміщення комірки пам’яті від початку сегменту. Адреси початків сегментів знаходяться у відповідних сегментах регістрах CS, DS, SS, ES – змінюючи вміст цих регістрів можна дістати доступ до тих чи інших ділянок пам’яті, тому такий запис легко зламати. Тому в процесорі 386 в захащеному режимі роботи адреси початків сегментів, їх довжини та права доступу зберігаються в спеціальних системних одиницях. В сегментних регістрах зберігають тільки індекс структури всередині таблиці. Це дозволило ізолювати сегменти та обмежило доступ до них з програми. Адже для доступу потрібно знати не тільки вміст сегменту регістру, але й тієї структури на яку він вказує. Така зміна можлива тільки при використанні привілейованих команд, що виконується тільки в певних умовах, можуть виконуватись в ОС, але не можуть використовуватись в програмах користувача. Паралельно з цим збільшення розміру сегментів:амід 64Кб для 86, до 4Гб для 486. Крім того програма буде виконуватись, якщо змінити адресу початку сегменту. Це означає, що в пам’яті він буде перенесений в інше місце. При цьому відносне зміщення в сегменті не можна зміщувати. Це дозволить переносити програму в пам’яті навіть під час її виконання. Використання сегментації пам’яті дозволяє різко збільшити розміри пам’яті, яку можна використати. Так, в захащеному режимі максимально можливий об’єм пам’яті, яку можна адресувати – 64Тб. Це віртуальна пам’ять. Тоді як фізичний об’єм пам’яті складає 4Гб. В деяких випадках використовувати сегментацію недоцільно. Тому в старших моделях можна працювати із сегментами довжиною до 4Гб. Це означає, що вся фізична пам’ять розглядається як один сегмент. Повністю усі переваги сегментації пам’яті проявляється в захащеному режимі роботи. В реальному режимі роботи оперує з 4-ма сегментами:а 1. Сегмент коду; 2. Сегмент даних; 3. Сегмент стеку; 4. Сегмент додаткових даних. Для процесорів 386 з’явились ще 2 сегменти додаткових даних. Ці сегменти в пам’яті можуть розташовуватисьяк завгодно по відношенню один до одного. Ніяких обмежень на їх розташування немає. Сегменти можуть починатись з однієї адреси, тобто можуть перекриватись. Вони можуть розташиняватись в пам’яті без перекривання, або між ними можуть бути проміжки. Для визначення адреси наступної команди після виконання попередньої використовують 2 регістри: CS (регістр сегменту коду, він містить початок адресу цього сегменту), і IP(регістр вказівника команд, якість зміщення команди відносно початку сегменту). В процесі виконання програми вміст ІР автоматично змінюється. Якщо змінити вміст регістрів CS, та ІР, то виклик програми почнеться з іншої адреси. Але явним чином змінити вміст цих регістрів неможливо. Це можна зробити тільки при використані деяких команд. Наприклад при виклику підпрограми. Або виконані програми безумовного переходу. Як уже відмічалось в сегменті коду можна також описувати дані. Але це роблять тільки в обгрунтованих випадках, наприклад в програмі обробки переривань.
Початок програми В початковій версії необхідно було відмітити точку входу в програму деякою міткою. Ім’я цієї мітки потім вказувалось в директиві end. Це було ознакою закінчення файлу з програмою. У сучасних асемблерах для визначення точки входу в програму використовується директива startup. Вона записується в сегменті коду, це спрощує запис і, крім цього, автоматично ініціалізує значення сегментних регістрів. В старих версіях це потрібно було робити вручну
Кінець програми Для закінчення програми потрібно було скористяватись стандартною директивою. Вона робить відповідний виклик автоматично. Якщо програму необхідно закінчити спеціальним чином, наприклад встановити резидентну програму, то користяватись цією директивою не можна. В таких випадках потрібно явно викликати відповідну функцію операційної системи.
Змінні в програмі В усіх інших сегментах виділяється місце для змінних, що використовуються в програмі. Поділ сегментів на сегменти даних, стеку та додаткових даних зумовлена тим, що змінні, які тут визначені мають різні властивості. Змінні в програмі можна розбити на 2 великі групи: 1. Змінні, які явна використовуються в програмі виходячи з її логіки. Такі змінні як правило мають імена і використовуються вони для збереження даних, які мають наперед визначений зміст. Наприклад, змінна що використовується для збереження коду натиснутої клавіші. 2. Часто в програмі необхідно мати місце для збереження тимчасових даних
Сегмент даних Використовується для збереження змінних, що визначає програміст. Як правило це робиться при допомозі псевдооператорів виділення пам’яті. Для визначення адреси початку сегменту використовується регістр DS. Для визначення другої компоненти адреси (відповідного зміщення всередині сегменту) використовується кілька способів адресації. Зауважимо, що способів визначення адреси змінної набагато більше ніж способів визначення адреси команди. В мові асемблер є засоби для явного розділ. збереження змінних. Ці змінні можуть мати або не мати початкових значень, а також можуть бути змінними константами. Розділяти ці типи змінних не обов’язково. Усіх їх можна зберігати в сегменті даних
Ініціалізовані змінні Змінні, початкові значення яких відоме як правило записуються в сегменті, що починається директивою data. До цих змінних відносяться також ті, початкові значення яких відоме і в процесі роботи вони не повиннVпмінятись
Неініціалізовані змінні Неініціалізовані змінні записуються в сегмент який починається директивою data. Сегмент стеку використовується для збереження тимчасових даних для яких недоцільно виділяти змінні. Адреса початку цього сегменту розміщується в регістрі SS, а зміщення відносно початку в регістрі SP. Для визначення сегменту стеку використовується директива stack. Цей сегмент відрізняється від сегменту даних і коду. В сегменті даних і коду можна явно адресувати б-я комірку пам’яті. Тут значення регістру при виконанні команд. В кожний момент часу пара цих регістрів SS і SP вказує на так звану вершину стеку. Сюди можна записати або прочитати значення. Стек організований за правилом: перший записаний, останній прочитаний. Тому у програмі команди запису і зчитування зі стеку потрібно використовувати попарно. Стек переважно використовується для тимчасового зберігання адреси повернення з програми або перервань. Ще одне ми. За вання стеку – при його допомозі можна передавати аргументи підпрограмам і організовувати локальні змінні.
Моделі пам’яті 1. Tiny (файл *.com). При цьому регістри CS, DS, SS містять однакові значення. Це найбільш компактна модель пам’яті. Розмір пам’яті не може перевищувати 63Кб. Адресація здійснюється за допомогою зміщення і міток. Оскільки програма на асемблері не є великими, то це не є значним обмеженням. Ця модель широко використовується, особливо в резидентних програмах. 2. Small. Сегмент коду відділений від сегменту стеку і сегменту даних. Сегмент даних і стеку об’єднані в єдине. Тому CS і DS мають однакові значення. Це найбільш поширена модель при розробці програм на асемблері. Всі переходи і виклики підпрограм здійснюються за допомогою короткої адресації. 3. Compact. Використовується 1 сегмент коду і можливе ми. За вання кількох сегментів даних. тому виклики підпрограм і переходи в програмі є короткими, а доступи до даних – далекими. Сегмент даних і стек об’єднані в одну групу. 4. Medium. В значній мірі протилежна до попередніх. Є кілька сегментів коду і один сегмент даних. Тому доступ до підпрограм і переходи в програмі є далекими (потрібна адреса сегменту та зміщення). А доступ до даних здійснюється за допомогою короткої адресації (вказується тільки зміщення). 5. Large. Це найбільш загальний спосіб організації пом’яті. Тут можуть використовуватись декілька сегментів коду і даних. Доступ до змінних, переходи на мітки здійснюються за допомогою адреси сегментів та зміщень. 6. Huge, Flat. Це досить поширені моделі. Така ж організація пам’яті, як і в large. Вона використовується для поєднання асемблера змовами високого рівня. В них можна задати таку модель пам’яті, з’являється можливість роботи з даними що займають більше 64 Кб. В програмі на асемблері таку модель використовувати недоцільно. Модель Flat відповідає моделі Small, різниця в тому, що використовуються 32 розрядні регістри. Таку модель можна організувати використовуючи процесор вище і80386. Переважно її використовують для роботи в захащеному режимі. В цьому випадку доступ до всієї області пам’яті здійснюються за допомогою одного сегменту. При програмуванні на асемблері краще використовувати перші 2 моделі пам’яті. Можна використовувати інші моделі, але в цих випадках програма ускладнюється, адже доводиться слідкувати за розміщенням кожного сегменту. Для визначення моделі пам’яті використовується директива model, що має такий формат запису: Model [ < модифікатор >] < тип >. В цій директиві може бути вказано ще ряд параметрів. Єдиним необхідним параметром є параметр <тип>. Тип можна додатково модифікувати. Для цього необхідно вказати один із 4 модифікаторів:аnearstack; farstack; use16; use32. При першому стек і дані розташинані в межах одного сегменту; farstack вказує, що сегмент даних буде розташинаний за межами стеку. Nearstack використовується по замовчуванню у всіх стандартних моделях пам’яті. Він дозволяє організувати інтерфейс з мовами високого рівня. Тут глобальні дані розміщені в сегменті даних, а локальні – в сегменті стеку. Два останні модифікатори використовуються тільки для МП 80386 і старше, вони вказують які сегменти використовуються в програмі, 16-розрядні (64 Кб сегмент) і 32-розрядні (4Гб сегмент). Доцільно використовувати ці модифікатори при розробці програм при використанні 32-розрядних компіляторів.
Спасибо, НІКОЛЯ, Вы сэкономили много букв в моем посте, и опрокинули ушат прохладной водицы на горячие головы! --- Пишу сей пост, оторвавшись в перерыве от своего Ассемблера. Язык нужный и полезный, для профи - обязателен. Только при чем тут школа? Для "подвинутых" деток - другое дело, пусть в кружке повозятся, хуже не будет. Им бы не помешал какой-нибудь учебный конструктор на микроконтроллере, жаль, что не производится ничего подобного.
Но основной массе школяров дай бог основы "пашкаля" освоить. Дайте им эти основы, и все будут вам в пояс кланяться, - и будущие студенты, и их преподаватели!