Все про Assembler українською мовою на сайті net.kr.ua

 

:: Меню ::

Головна
Введення
Архітектура реального режиму
Основи програмування
Команди і алгоритми
Розширені можливості сучасних мікропроцесорів
Система команд процесорів Intel
Книга для гостей
Контакти
Добавити у вибране

:: Друзі ::

 
 

:: Лічильники ::

=

 

 

 

 

fff00e50

Опис даних

Практично будь-яка програма містить в собі перелік даних, з якими вона працює. Це можуть бути символьні рядки, призначені для виводу на екран; числа, що визначають хід виконання програми або що беруть участь в обчисленнях; адреси підпрограм, обробників переривань або просто тих або інших полів програми; спеціальні коди, наприклад, коди кольору символів, що виводяться на екран, і так далі Окрім даних, визначуваних в тексті програми, в програму часто входять зарезервовані поля, призначені для заповнення по ходу виконання програми, наприклад, результатами обчислень або шляхом читання з файлу. Всі ці дані і зарезервовані поля мають бути визначені у складі сегменту даних програми (в принципі вони можуть бути визначені, і часто визначаються, не в сегменті даних, а в сегменті команд, але тут ми не стосуватимемося цього питання).
Для визначення даних використовуються, головним чином, три директиви асемблера: db (define byte, визначити байт) для запису байтів, dw (define word, визначити слово) для запису слів і dd (define double, визначити подвійне слово) для запису подвійних слів:

db 255


dw 6.5535


dd 100000000

Окрім перерахованих, є і інші директиви, наприклад df (define fanvord, визначити поле з 6 байт), dq (define quadword, визначити четверне слово) або dt (define tcraword, визначити 10-байтову змінну), але вони використовуються значно рідше.
Для того, щоб до даних можна було звертатися, вони повинні мати імена. Імена даних можуть включати латинські букви, цифри (не як перший знак імені) і деякі спеціальні знаки, наприклад, знаки підкреслення (_), долара ($) і комерційного at (@). Довжину імені деякі асемблери обмежують (наприклад, асемблер MASM - 31 символом), інші - ні, але у будь-якому випадку дуже довгі імена утрудняють читання програми. З іншого боку, імена даних слід вибирати так, щоб вони відображали призначення конкретного даного, наприклад counter для лічильника або filename для імені файлу:

counter dw 10000


filename db "a:\myfile.001'

Значення числових даних можна записувати в різних системах числення; частіше за інших використовуються десяткова і 16-річная запис:

size dw 256 ;В осередок size записується
                    ;десяткове число 256


setb7 db 80h ;В осередок setb7 записується
                    ;16-річноє число 80h

Необхідно відзначити неточність приведених вище коментарів. У пам'яті комп'ютера можуть зберігатися тільки двійкові коди. Якщо ми говоримо, що в якомусь осередку записано десяткове число 128, ми маємо на увазі не фізичний вміст осередку, а лише форму представлення цього числа в початковому тексті програми. У слові з ім'ям size фактично буде записаний двійковий код 0000000100000000, що є двійковим еквівалентом десяткового числа 256. У другому випадку в байті з ім'ям setbit? буде записаний двійковий еквівалент шістнадцяткового числа 80h, який складає 10000000 (тобто байт зі встановленим бітом 7, звідки і отримав ім'я цей осередок).
Для резервування місця під масиви використовується оператор dup (duplicate, дублювати), який дозволяє "розмножити" байт, слово або подвійне слово задане число разів:

rawdata dw 300 dup (1)   ;Резервируются 300 слів
                                        ;заповнених числом 1


string db 80 dup ('^')        ;Резервируются 80 байтів
                                        ;заповнених знаком '^'

Привласнення даним символічних імен дозволяє звертатися до них в програмних пропозиціях, не піклуючись про фактичні адреси цих даних. Наприклад, команда

mov Ax,size

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

data segment


0000h counter dw 10000


0002h pages db "Сторінка 1"


000ch numbers db 0, 1, 2, 3, 4


0011h page_addr dw pages


data ends

Сегмент даних починається з даного на ім'я counter, яке описане, як слово (2 байт) і містить число 10000. Очевидно, що його зсув дорівнює 0. Оскільки це дане займає 2 байт, наступне за ним дане pages отримало зсув 2. Дане pages описує рядок тексту завдовжки 10 символів і займає в пам'яті стільки ж байтів, тому наступне дане numbers отримало відносну адресу 2 + 10 = 12 = Ch. У полі numbers записано 5 байтових чисел, тому останнє дане сегменту з ім'ям page_addr розміщується за адресою Ch + 5 = 11h.
Асемблер, починаючи трансляцію сегменту (в даному випадку сегменту даних) починає відлік його відносних адрес. Цей відлік ведеться в спеціальній змінній транслятора (не програми!), яка називається лічильником поточної адреси і має символічне позначення знаку долара (S). У міру обробки полів даних, їх символічні імена зберігаються в створюваній асемблером таблиці імен разом з відповідними ним значеннями лічильника поточної адреси. Іншими словами, введені нами символічні імена отримують значення, рівні їх зсувам. Таким чином, з погляду транслятора counter рівне 0, pages - 2, numbers - Ch і так далі Тому пропозиція

page_addr dw pages

трактується асемблером, як

page_addr dw 2

і приводить до запису в слово з відносною адресою 11h числа 2 (зсуви рядка pages).
Приведені міркування доводиться використовувати при зверненні до "нутрощів" оголошених даних. Хай, наприклад, ми хочемо виводити на екран рядка "Сторінка 2", "Сторінка 3", "Сторінка 4" і так далі Можна, звичайно, всі ці рядки описати в сегменті даних окремо, але це приведе до марної витрати пам'яті. Економніше поступити по-іншому: виводити на екран один і той же рядок pages, але модифікувати в ній номер сторінки. Модифікацію номера можна виконати за допомогою, наприклад, такої команди:

mov pages + 9 ' 2'

Тут ми "уручну" визначили зсув символу, що цікавить нас, в рядку, знаючи, що всі дані розміщуються асемблером один за одним в порядку їх оголошення в програмі. При цьому, якого б значення не набуло ім'я pages, вираз pages + 9 завжди відповідатиме байту з номером сторінки.
Таким же прийомом можна скористатися при зверненні до даного numbers, яке по суті є невеликим масивом з 5 чисел. Адреса першого числа в цьому масиві рівна просто numbers, адреса другого числа - numbers + 1, адреса третього - numbers + 2 і так далі Наступна команда прочитає останній елемент цього масиву в регістр DL:

mov Dl,numbers+4

Який сенс мало об'єднання ряду чисел в масив numbers? Та ніякого, якщо до цих чисел ми все одно звертаємося окремо. Зручніше було оголосити цей масив таким чином:

nmb0 db 0


nmbl db 1


nmb2 db 2


nmb3 db 3


nmb4 db 4

В цьому випадку для звернення до останнього елементу не треба обчислювати його адресу, а можна скористатися ім'ям nmb4. Якщо, з іншого боку, ми хочемо працювати з числами, як з масивом, використовуючи індекси окремих елементів (про що мова йтиме пізніше), те привласнення масиву загального імені представляється природним. Отримання останнього елементу масиву по його індексу виконується за допомогою такої послідовності команд:

mov Si,4                   ;Индекс елементу в масиві


mov Dl,numbers[SI];Обращение за адресою


                                  ;numbers + вміст SI

Іноді бажано звертатися до елементів масиву (зазвичай невеликого розміру) то за допомогою індексів, то по їх іменах. Для цього треба до опису масиву, як послідовності окремих даних, додати додатковий символічний опис адреси початку масиву за допомогою директиви асемблера label (влучна):

numbers       label      byte


nmb0            db          0


nmbl             db          1


nmb2            db          2


nmb3            db          3


nmb4            db          4

Влучна numbers має бути оголошена в даному випадку з описувачем byte, оскільки дані, наступні за цією міткою, описані як байти і ми плануємо працювати з ними саме як з байтами. Якщо нам потрібно мати масив слів, то окремі елементи масиву слід оголосити за допомогою директиви dw, а мітці numbers додати описувач word:

numbers     label    word


nmb0          dw        0


nmbl           dw        1


nmb2          dw        2


nmb3       
    dw        3


nmb4          dw        4

У чому полягає відмінність двох останніх описів даних? Відмінність є, і вельми істотне. Хоча в обох випадках в пам'ять записується натуральний ряд чисел від 0 до 4, проте в першому варіанті під кожне число в пам'яті відводиться один байт, а в другому - слово. Якщо ми надалі змінюватимемо значення елементів нашого масиву, то в першому варіанті кожному числу' можна буде задавати значення від 0 255, а в другому - від 0 до 65535.
Вибираючи для даних спосіб їх опису, необхідно мати на увазі, що асемблер виконує перевірку розмірів використовуваних даних і не пропускає команди, в яких робиться спроба звернутися до байтів, як до слів, або до слів - як до байтів. Розглянемо останній варіант опису масиву numbers. Хоча під кожен елемент виділено ціле слово, проте реальні числа невеликі і цілком помістяться в байт. Може виникнути спокуса попрацювати з ними, як з байтами, перенісши заздалегідь в байтові регістри:

mov Al,nmb0 ;Переносим nmb0 в AL


mov Dl,nmbl ;Переносим nmb1 в AL


mov Cl,nmb2 ;Переносим nmb2 в AL

Так робити не можна. Транслятор повідомить про грубу помилку - невідповідність типів, і не створюватиме об'єктний файл. Проте досить часто виникає реальна потреба в операціях такого роду. Для таких випадків передбачений спеціальний атрибутивний оператор byte ptr (byte pointer, байтовий покажчик), за допомогою якого можна на час виконання однієї Команди змінити розмір операнда:

mov Al,byte ptr nmb0


mov Dl,byte ptr nmbl


mov Cl,byte ptr nmb2

Ці команди транслятор розглядає, як правильні.
Часто виникає необхідність виконати зворотну операцію - до пари байтів звернутися, як до слова. Для цього треба використовувати оператора word ptr:

okey db 'OK'


.


mov Ax,word ptr okey

Тут обидва байти з байтової змінної okey переносяться в регістр АХ. При цьому перший по порядку байт, тобто байт з меншою адресою, що містить букву "О" (можна вважати, що він є молодшим в слові
"OK"), відправиться в молодшу половину АХ - регістр AL, а другий по порядку байт, з буквою "К", займе регістр АН.
До цих пір мова йшла про дані, які, по суті, були змінними, в тому сенсі, що під них виділялася пам'ять і їх можна було модифікувати. Мова асемблера дозволяє також використовувати константи, які є символічними позначеннями чисел і можуть використовуватися усюди в тексті програми, як наочні еквіваленти цих чисел:

maxsize = 0ffffh


mov Cx,maxsize mov Cx,0ffffh

Останні дві команди повністю еквівалентні.
При визначенні констант допустиме виконання арифметичних операцій. Хай нам треба задати позицію символу (або рядки символів) на екрані. Враховуючи, що кожен символ записується у відеопам'яті в двох байтах (у першому - код ASCII символу, а в другому - його атрибут), рядок екрану має довжину 80 символів, а висота екрану складає 25 рядків, то для виведення деякого символу в середину екрану його зсув у відеопам'яті від початку відеосторінки можна визначити таким чином:

position=80*2*12+40*2

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

mes db 'Чекайте'


mes_len = $-mes

В даному прикладі константа mes_len набуває значення довжини рядка mes (в даному випадку 5 байт), яка обчислюється як різниця значення лічильника поточної адреси після визначення рядка і її початкової адреси mes. Такий спосіб зручний тим, що при зміні вмісту рядка досить перетранслювати програму, і та ж константа mes_len автоматично набуде нового значення.

-

:: Наша кнопка ::

Отримати код:

Підтримайте наш сайт і розмістіть нашу кнопку на своєму ресурсі.


:: Популярне ::

-


:: Посилання ::

-


 

 

 


Copyright © net.kr.ua, 2019-2025 (assem.us)