Записи в мові асемблер
Наша господиня-програміст стає дедалі економнішою. Вона вже хоче працювати з продуктами на молекулярному рівні, без будь-яких відходів та марних витрат.
Подумаємо, навіщо витрачати під деякий програмний індикатор зі значенням "включено-вимкнено" цілих вісім розрядів, якщо вистачає одного? А якщо таких індикаторів кілька, то витрата оперативної пам'яті може стати дуже відчутною.
Коли ми знайомилися з логічними командами, то говорили, що їх можна застосовувати для вирішення подібної проблеми. Але це не дуже ефективно, тому що велика ймовірність помилок, особливо при складанні бітових масок.
TASM надає нам спеціальний тип даних, використання якого допомагає вирішити проблему роботи з бітами ефективніше. Йдеться про спеціальний тип даних записи.
Запис - структурний тип даних, що складається з фіксованого числа елементів довжиною від одного до декількох біт.
При описі запису кожного елемента вказується його довжина в бітах і, що необов'язково, деяке значення.
Сумарний розмір запису визначається сумою розмірів її полів і може бути більше 8, 16 чи 32 біт.
Якщо сумарний розмір запису менше зазначених значень, всі поля записи “притискаються” до молодшим розрядів.
Використання записів у програмі, як і, як і структур, організується у три етапи:
- Завдання шаблону запису , тобто визначення набору бітових полів, їх довжин та, за необхідності, ініціалізація полів.
- Визначення екземпляра запису. Як і структур, цей етап передбачає ініціалізацію конкретної змінної типом заздалегідь визначеної з допомогою шаблону записи.
- Організація звернення до елементів запису.
Компілятор TASM, крім стандартних засобівобробки записів, підтримує також деякі додаткові можливості їх обробки.
Опис шаблону запису має наступний синтаксис (рис. 6):
є послідовністю описів окремих елементів запису згідно з синтаксичною діаграмою (див. рис. 6):
Мал. 6. Синтаксис опису шаблону запису
При описі шаблону пам'ять не виділяється, так як це лише інформація для транслятора асемблера про структуру запису.
Так само, як і для структур, розташування шаблону в програмі може бути будь-яким, але при цьому необхідно враховувати логіку роботи однопрохідного транслятора.
Визначення екземпляра запису
Для використання шаблону запису у програмі необхідно визначити змінну з типом цього запису, для чого застосовується наступна синтаксична конструкція (рис. 7):

Мал. 7. Синтаксис опису екземпляра запису
Аналізуючи цю синтаксичну діаграму, можна дійти невтішного висновку, що ініціалізація елементів записи здійснюється досить гнучко. Розглянемо кілька варіантів ініціалізації.
Якщо ініціалізувати поля не потрібно, достатньо вказати ? при визначенні екземпляра запису:
Якщо ви складете та дослідите у налагоджувачі тестовий приклад з даним визначенням запису, то побачите, що всі поля змінної типу запис flag обнулюються. Це відбувається, незважаючи на те, що у визначенні запису задані початкові значення полів.
Якщо потрібна часткова ініціалізація елементів, вони полягають у кутові ( і > ) чи фігурні ( < і >) дужки.
Відмінність тут у тому, що у кутових дужках елементи мають бути задані в тому самому порядку, що й у визначенні запису. Якщо значення деякого елемента збігається з початковим, його можна невказувати, але обов'язково позначити його комою. Для останніх елементів коми, що йдуть поспіль, можна опустити.
Наприклад, погодитися із значеннями за умовчанням можна так:
flag iotest <> ;погодилися зі значенням за умовчанням
Змінити значення поля i2 можна так:
flag iotest; перевизначили i2
Застосовуючи фігурні дужки, також можна вказати вибіркову ініціалізацію полів, але при цьому необов'язково позначати коми поля, зі значеннями за умовчанням яких ми згодні:
flag iotest ;перевизначили i2, не звертаючи уваги на порядок
;слідування інших компонентів запису
Робота із записами
Насамперед для розуміння проблеми потрібно засвоїти кілька моментів:
- Кожному імені елемента запису асемблер надає числове значення, що дорівнює кількості зрушень вправо, які потрібно зробити для того, щоб цей елемент виявився притиснутим до початку осередку. Це дає нам можливість локалізувати його та працювати з ним. Але для цього потрібно знати довжину елемента у бітах.
- Зсув вправо здійснюється за допомогою команди зсуву shr.
- Асемблер містить оператор width , який дозволяє дізнатися розмір елемента запису в бітах або розмір запису. Варіанти застосування оператора width:
width имя_элемента_записи ;значенням оператора буде розмір елемента в бітах.
width имя_типа_запису ;значенням оператора буде розмір всього запису в бітах.
mov ax,width iotest
- Асемблер містить оператор mask, який дозволяє локалізувати біти потрібного елемента запису. Ця локалізація здійснюється шляхом створення маски, розмір якої збігається з розміром запису. У цій масці обнулені біти на всіх позиціях, за винятком тих, якізаймає елемент у записі.
- Самі дії перетворення елементів запису проводяться за допомогою логічних команд.
Тепер у вас є вся інформація про засоби асемблера для роботи із записами.
Ви також зрозуміли, що безпосередньо звернутися до елемента запису неможливо. Щоб зробити обробку цікавого для нас елемента, потрібно спочатку виділити, зрушити його, при необхідності, до молодших розрядів, виконати необхідні дії і помістити його назад на своє місце в записі. Тому, щоб вам не винаходити щоразу велосипеда, далі ми опишемо типові алгоритми здійснення цих операцій над елементами запису.
Ваше завдання - закодувати ці алгоритми тим чи іншим способом відповідно до вимог задачі.
Виділення елемента запису:
- Помістити запис у тимчасову пам'ять - регістр (8, 16 або 32-бітний залежно від розміру запису).
- Отримати бітову маску, яка відповідає елементу запису, за допомогою оператора mask .
- Локалізувати біти в регістрі за допомогою маски та команди and .
- Зрушити біти елемента до молодших розрядів регістру командою shr. Число розрядів для зсуву одержати з використанням імені елемента запису.
В результаті цих дій елемент запису буде локалізовано на початку робочого регістру і далі з ним можна робити будь-які дії.
Робота з елементом запису:
Як ми вже з'ясували, з елементами запису виконуються будь-які дії, як над звичайною бінарною інформацією.
Єдине, що потрібно відстежувати, це розмір бітового поля. Якщо, наприклад, розмір поля збільшиться, то згодом може статися зміна сусідніх полів бітів. Тому бажано виключити зміну розміру поля.
Приміщеннязміненого елемента на його місце у запис:
- Використовуючи ім'я елемента запису як лічильник зрушень, зрушити ліворуч біти елемента запису.
- Якщо ви не впевнені в тому, що розрядність результату перетворень не перевищила вихідну, можна виконати обрізання зайвих бітів, використовуючи команду and і маску елемента.
- Підготувати вихідний запис до вставки зміненого елемента шляхом обнулення бітів у записі дома цього елемента. Це можна зробити шляхом накладання командою та інвертованої маски елемента запису на вихідний запис.
- За допомогою команди або накласти значення в регістрі на вихідний запис.
Як приклад розглянемо лістинг 8, який обнуляє поле i2 запису iotest.
Лістинг 8. Робота з полем запису
iotest record i1:1,i2:2=11,i3:1,i4:2=11,i5:2=00