Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lec_12_pres.doc
Скачиваний:
3
Добавлен:
05.12.2018
Размер:
246.27 Кб
Скачать

3. Короткі та довгі вказівники. Модифікатори вказівників

Молодші моделі пам'яті використовують один спільний сегмент для всіх даних програми. Вони передбачають, що сегментна частина адрес усіх даних зберігається в системному регістрі DS.

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

За замовчуванням двобайтовий формат встановлено для всіх вказівників у молодших моделях пам'яті (див. табл. 1).

(поточна межа ДП)

Зовнішня ДП

SS:FFFF

Динамічні дані

SS:SP

Локальні дані (стек)

Сегмент стека (<64К)

SS:0000

(поточна межа стека)

DS:0000

Глобальні і статичні дані

Сегмент даних (<64К)

CS(k):0000

Код програмного файла k

<64К Сегменти

<64К програмних

< 64 К кодів

CS(1):0000

Код програмного файла 1

Рис. 2. Схема розподілу оперативної пам'яті в моделі Large

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

Вказівники старших моделей називають довгими (чи дальніми) або far-вказівниками. Коли відбувається звертання до об'єкта через far-вказівник, сегмент адреси записується у відповідний сегментний регістр для формування фізичної адреси цього об'єкта.

Довгі вказівники дають змогу звернутись до кожного байта оперативної пам'яті в межах перших 1 Мбайт.

Формат вказівника, встановлений автоматично за заданою моделлю пам'яті, можна змінити, використовуючи модифікатори near, far або huge (всі три є зарезервованими словами Borland C). Модифікатор формату записують після базового типу вказівника перед символом *, наприклад:

char far* psymb;

near-, far- та huge-вказівники.

Вказівники з модифікатором near є 16-розрядними беззнаковими цілими числами, що зберігають тільки зміщення адреси, їх доцільно застосовувати в програмах, сумарний обсяг даних яких не перевищує обсягу сегмента (64 Кбайт).

Застосування коротких вказівників у багатосегментних програмах вимагає перевизначення сегментного регістра DS або використання спеціальних форм near-вказівників, про які мова йтиме далі.

Вказівники з модифікатором far 32-розрядні, вони зберігають повну адресу об'єкта, тобто сегмент і зміщення. Такі вказівники застосовують у молодших моделях пам'яті, коли потрібно звернутись до об'єктів, розташованих за межами сегмента коду програми чи сегмента даних. Зокрема, far-вказівники використовують для роботи з даними у зовнішній динамічній пам'яті, для безпосереднього звертання до відеопам'яті, для доступу до адресного простору BIOS тощо.

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

/* Функція виведення символа в позиції екрана (80,25) */

void PutLastScrSmb (char symb, char attr)

{

/* symb - код символа; attr - атрибути: колір символа і колір фону */

char far* VMadr = (char far *)0xb8000000; /* початок відеопам'яті */

VMadr += (24*80+79)*2; /* адреса останнього символа екрана */

*VMadr = symb; /* відображення символа */

if (attr) /* якщо треба змінити кольори */

*(VMadr+l) =attr; }

У разі роботи з довгими вказівниками часто виникає потреба поділу значення адреси на складові частини: сегмент і зміщення, чи, навпаки, необхідність формування довгого вказівника зі складових частин. Для виконання цих операцій призначені спеціальні макроси, оголошені в заголовному файлі <dos .h>:

unsigned FP_SEG (void far* ptr);

unsigned FP_OFF (void far* ptr);

void far* MK_FP (unsigned seg, unsigned off);

Два перших макроси FP_SEG () і FP_OFF () виділяють з адреси, занесеної у far-вказівник ptr, відповідно сегментну частину та зміщення. Макрос MK_FP () формує значення довгого вказівника зі заданого сегмента seg і зміщення off. За допомогою цього макроса адресу останнього символу екранного зображення, яка використовувалася у функції PutLastScrSmb (), можна сформувати, наприклад, так:

char far* VMadr= (char far*)MK_FP(Oxb800, 25*80*2-2);

Якщо тепер оголосити:

unsigned of s = FP_OFF( VMadr); /* виділення зміщення */

то змінна ofs отримає значення 3998, що відповідає зміщенню в байтах (відносно початку відеопам'яті) знакомісця екрана з координатами (80, 25).

Іншим способом формування довгих адрес та виділення їх складових частин є використання структур і об'єднань. Такий підхід дає змогу одночасно виконувати перетворення базового типу вказівника, тобто змінювати форму доступу до даних.

Розглянемо два наступних оголошення:

struct mem_addr {

unsigned ofs, segm; /* зміщення та сегмент фізичної адреси */

};

union addr_convert {

struct mem_addr adr;

char far *pb; /* вказівники */

unsigned far *pw; /* на дані */

unsigned long far *pl; } /* різних типів */

uptc;

}

Змінна uptc має тип об'єднання union addr_convert, до складу якого входять структура adr і три вказівники з різними базовими типами: pb, pw і рl. Поля ofs і segm структури adr задають зміщення і сегмент потрібної фізичної адреси.

Оскільки в far-вказівниках сегментна компонента займає два старші байти, а зміщення-два молодші, то поля структури adr треба записувати у відповідному порядку: спочатку зміщення, а потім сегмент адреси.

Зміна значень полів структури adr викликає відповідну зміну значення кожного з вказівників, оголошених у полях об'єднання uptc.

Наприклад, наступні присвоєння:

uptc.adr.segm = 0x0; /* сегмент адреси */

uptc.adr.ofs = 0x0450; /* зміщення адреси */

сформують адресу 0000:0450h, починаючи з якої в оперативній пам'яті зберігається інформація BIOS про поточні координати текстового курсора для кожної з восьми сторінок відеопам'яті. Тепер до цих BIOS-даних можна звертатись побайтово, використовуючи вказівник pb, двобайтовими словами через вказівник pw або чотирибайтовими словами через вказівник рl. Зокрема, поточну позицію курсора на нульовій (основній) відеосторінці можна отримати так:

cur_x = *uptc.pb; /* горизонтальна координата */

cur_y = * (uptc.pb+1); /* вертикальна координата */

або можна зчитати відразу обидві координати:

cur_pos = *uptc.pw; /* читання двобайтового слова */

Використання far-вказівників пов'язане з певними проблемами, про які необхідно пам'ятати, розробляючи програми з цими вказівниками.

По-перше, одну і ту ж фізичну адресу можна задати різними значеннями вказівника. Обидва оголошені вказівники:

unsigned far* adr1= (unsigned far*)0x00000410;

unsigned far* adr2= (unsigned far*)0x00410000;

задають спільну адресу початку області даних відеопараметрів BIOS. Проте в разі порівняння цих вказівників: adr1==adr2 - результат буде хибним, бо порівнюються числові значення, записані в adr1 та adr2. А в разі порівняння far-вказівників операціями <, <=, >, >= перевіряються тільки зміщення, тобто молодші половини адрес.

Другою проблемою є міжсегментний перехід. Якщо до адреси far-вказівника додати певне число, то змінюється тільки зміщення адреси, а перенесення в сегментну половину не відбувається.

Вказаних недоліків не мають вказівники з модифікатором huge. В операціях з цими вказівниками беруть участь усі 32 розряди їхніх значень.

Після кожної операції виконується нормалізація вказівника: значення сегмента коректується так, щоб значення зміщення не перевищувало 15.

Наприклад, адреса 8C65:12A4h у нормалізованій формі записується так: 8D8F:0004h. Нормалізація забезпечує однозначність запису кожної фізичної адреси й коректність усіх операцій порівняння та адресної арифметики, в тому числі пов'язаних із міжсегментними переходами.

Тому для безпомилкової роботи з блоками даних, обсяги яких перевищують обсяг сегмента, треба використовувати huge-вказівники, хоча при цьому дещо збільшується час виконання програми через додаткові операції нормалізації.

Наступна функція застосовує huge-вказівник для визначення суми всіх машиних слів адресного простору BIOS, починаючи з адреси F000:0000h до FFFF:000Fh включно; сума є унікальною для кожної серії персональних комп'ютерів і може бути використана в програмах захисту інформації.

/* Функція обчислення суми слів BIOS-області. Варіант 1 */

unsigned long Summa_BIOS (void)

{

unsigned long sum=0;

unsigned huge* ph =(unsigned huge*)0xf0000000; /* початок області */

while (ph) /* остання адреса - ffff:000fh */

sum += *ph++;

return sum;

}

cs-, ds-, _ss- та _es-вказівники

Ефективність безпосереднього програмування оперативної пам'яті зростає у разі застосування спеціальних коротких вказівників, що оголошуються через модифікатори _cs, _ds, _ss та _es (ці специфікатори теж є службовими словами Borland C).

Короткі вказівники зберігають тільки зміщення фізичних адрес, а сегментна частина адреси береться з відповідного системного регістра: CS, DS, SS чи ES.

Для запису в сегментні регістри необхідної адреси початку сегмента використовують спеціальні псевдорегістрові змінні: _CS, _DS, _SS та _ES. Нижче подано альтернативний варіант функції обчислення суми машинних слів адресного простору BIOS, в якому для звертання до даних використано спеціальний короткий вказівник _es.

Час виконання даного варіанта функції істотно менший, ніж попереднього, де використовувався huge-вказівник.

/* Функція обчислення суми слів BIOS-області. Варіант 2 */

unsigned long BIOS_summa(void)

{

unsigned _es * p_es = (unsigned _es*)0 /* початкове зміщення */

unsigned long sum;

_ES=0xf000; /* сегмент адреси BIOS-області */

for (sum=*p_es++; *p_es; p_es++)

sum += *p_es; /* сума даних усього сегмента */

return sum;

}

Застосування спеціальних near-вказівників та відповідних псевдорегістрових змінних вимагає особливої обережності, оскільки в процесі виконання програми значення сегментних регістрів можуть змінюватись.

У цьому випадку, щоб уникнути формування помилкових фізичних адрес, перед наступним звертанням до даних через near-вказівники з модифікаторами _cs, _ds, _es або _ss необхідно відновити потрібне значення відповідного сегментного регістра. Нижче наведено ще два приклади застосування _es-вказівників у функціях зміни та копіювання текстових відеозображень.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]