Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Конспект лекцій із Системного програмування та...docx
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
786.31 Кб
Скачать

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 треба кожному сегменту ставити в відповідність сегментний регістр: якщо асемблеру зустрінеться посилання на ім'я із сегмента, якому не відповідає ніякий сегментний регістр, то він зафіксує помилку. Правда, в обох випадках можна уникнути помилки, але для цього в посиланнях необхідно явно вказувати сегментний регістр.