
- •"Програмне забезпечення автоматизованих систем"
- •1. Оперативна пам'ять
- •2. Регістри
- •3. Прапори
- •1. Переходи
- •2. Безумовні переходи
- •3. Умовні переходи
- •4. Команди керування циклом
- •1. Подання даних арифметичні операції
- •2. Цілі числа без знака
- •3. Цілі числа зі знаком
- •4. Особливості виконання арифметичних операцій
- •5. Подання символів і рядків
- •6. Подання адрес
- •7. Директиви визначення даних
- •1. Структура команд. Виконавчі адреси
- •2. Формати команд
- •3. Запис команд в masm
- •1. Сегменти пам’яті, сегментні регістри
- •2. Сегментні регістри по домовленості
- •3. Сегментування, базування й індексування адрес
- •4. Програмні сегменти. Директива assume
- •5. Початкове завантаження сегментних регістрів
- •6. Посилання вперед
- •1. Строкові операції
- •1. Стек
- •2. Основні стекові команди
- •1. Організація процедур та переривань
- •2. Передача параметрів процедурам через регістри, через стек, через таблиці, через глобальні області
- •3. Передача параметрів по ссылке
- •4. Передача параметрів по повернутому значенню
- •5. Передача параметрів в стеку
- •6. Локальні змінні
- •1. Процеси і потоки
- •1.1 Розподіл часу між потоками
- •1.2 Динамічна зміна рівня пріоритету потоку
- •2. Робота із процесами й потоками в Win32 api
- •1. Адресний простір процесу
- •2. Керування віртуальною пам'яттю. Vmm
- •3. Файли даних, що проектуються на згадку
- •4. Взаємодія процесів через загальну область даних. Когерентність
- •5. Купи
- •1. Визначення вікна. Компоненти й параметри вікон
- •2. Клас вікна
- •3. Ієрархія вікон
- •1. Обмін даними між процесами
- •2. Вилучений виклик процедур (rpc - Remote Procedure Call)
- •3. Синхронізація потоків
- •1. Загальний порядок обробки виключень
- •2. Фільтри й оброблювачі виключень
- •1. Загальна структура системного реєстру
- •2. Робота додатків із системним реєстром
- •1. Файлові структури
- •2. Робота з томами й каталогами
- •3. Синхронна робота з файлами
- •4. Асинхронна робота з файлами
- •1. Системні файли ntfs
- •2. Master File Table (mft)
- •3. Цілісність даних і здатність до самовідновлення.
- •4. Організація й керування дисками
3. Сегментування, базування й індексування адрес
Оскільки сегментування адрес – це різновид модифікації адрес, то в ПК адреса, що вказується в команді, у загальному випадку модифікується по трьох регістрах - сегментному, базовому й індексному. У цілому, модифікація адреси виробляється у два етапи. Спочатку враховуються тільки базовий й індексний регістри (якщо вони, звичайно, зазначені в команді), причому обчислення тут відбувається в області 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.
4. Програмні сегменти. Директива assume
Розглянемо, як сегментування проявляється в програмах на MASM.
Для того щоб вказати, що деяка група команд програми на MASM утворить єдиний сегмент пам'яті, вони оформляються як програмний сегмент: перед ними ставиться директива SEGMENT, після них - директива ENDS, причому на початку обох цих директив повинне бути зазначене одне й те ж ім'я, що грає роль імені сегмента. Програма ж у цілому являє собою послідовність таких програмних сегментів, в кінці якої вказується директива кінця програми END, наприклад:
DT1 SEGMENT ;програмний сегмент з ім'ям DT1
A DB 0
B DW ?
DT1 ENDS;
DT2 SEGMENT ;програмний сегмент DT2
C 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, C) і мітки (BEG) ставляться до адресних виражень, і їм ставиться у відповідність адреса їхнього осередку відносно "свого" сегменту: імені A відповідає адреса 0, імені B - адреса 1, імені C - адреса 0, а мітці BEG - адреса 0.
Всі посилання на команди одного програмного сегмента асемблер сегментує за замовчуванням по тому самому сегментному регістру. По якому саме - встановлюється спеціальною директивою ASSUME. У нашому
прикладі ця директива визначає, що всі посилання на сегмент CODE повинні, якщо явно не зазначений сегментний регістр, сегментуватися по регістру CS, всі посилання на DT1 - по регістру DS, а всі посилання на DT2 – по регістру ES.
Зустрівши в тексті програми посилання на яке-небудь ім'я (наприклад, на ім'я C у команді MOV AX,C), асемблер визначає, у якому програмному сегменті воно описано (у нас - в DT2), потім за інформацією з директиви ASSUME довідається, який сегментний регістр поставлений у відповідність цьому сегменту (у нас - це ES), і далі утворить адресну пару із даного регістра й зсуву імені (у нас - ES:0), що і записує у формульовану машинну команду. При цьому асемблер ураховує використовувану в ПК угоду про сегментні регістри за замовчуванням: якщо в адресній парі, побудованої ним самим або явно заданої в програмі, сегментний регістр збігається з регістром за замовчуванням, то в машинну команду заноситься лише зсув. Якщо, скажемо, у нашому прикладі зустрінеться команда MOV CX,B, тоді по імені В асемблер побудує пари DS:1, але якщо операнд-адрес команди MOV за замовчуванням сегментується по регістру DS, то записувати цей регістр у машинну команду зайво й асемблер записує у неї тільки зсув 1.
Таким чином, директива ASSUME рятує програмістів від необхідності виписувати повні адресні пари не тільки тоді, коли використовуються сегментні регістри за замовчуванням (як у випадку з ім'ям B), але й тоді, коли в машинній команді потрібно було б явно вказати сегментний регістр (як у випадку з ім'ям C). В MASM сегментний регістр у посиланні на ім'я потрібно вказувати лише тоді, коли ім'я повинне по яким-небудь причинам сегментуватися по регістрі, відмінному від того, що поставлено у відповідність всьому сегменту, у якому це ім'я описане.
Однак все це справедливо тільки при дотриманні наступних умов. По-перше, директива ASSUME повинна бути зазначена перед першою командою програми. У іншому випадку асемблер, що переглядає текст програми зверху вниз, не буде знати, як сегментувати імена з команд, розташованих до цієї директиви, і тому зафіксує помилку. По-друге, у директиві ASSUME треба кожному сегменту ставити в відповідність сегментний регістр: якщо асемблеру зустрінеться посилання на ім'я із сегмента, якому не відповідає ніякий сегментний регістр, то він зафіксує помилку. Правда, в обох випадках можна уникнути помилки, але для цього в посиланнях необхідно явно вказувати сегментний регістр.