ТА_Методички / Lec_6
.pdfСтарші моделі: Compact, Large і Huge використовують окремий сегмент для стеку тa лише зовнішню динамічну пам'ять.
Для великих багатофайлових програм доцільно застосовувати розподіл пам'яті за моделлю Large, яка зберігає код кожного програмного файла в окремому сегменті (рис. 2).
Якщо ж обсяг статичних даних програми перевищує 64 Кбайт, то слід встановити модель Huge.
11/37
3. Короткі та довгі вказівники. Модифікатори вказівників
Молодші моделі пам'яті використовують один спільний сегмент для всіх даних програми. Вони передбачають, що сегментна частина адрес усіх даних зберігається в системному регістрі DS.
Тому для адресації змінних достатньо задавати тільки два байтові значення зміщень. Відповідні вказівники, які зберігають зміщення фізичних адрес, називають короткими (чи ближніми) або near-вказівниками.
За замовчуванням двобайтовий формат встановлено для всіх вказівників у молодших моделях пам'яті (див. табл. 1).
12/37
|
|
(поточна межа ДП) |
|
|
Зовнішня ДП |
SS:FFFF |
|
Динамічні дані |
|
|
|
|
|
|
|
||
|
|
|
|
|
|
SS:SP |
|
Локальні дані (стек) |
|
Сегмент стека |
|
|
|
|
|
|
|
|
|
(поточна межа стека) |
|
(<64К) |
|
SS:0000 |
|
Сегмент даних |
|||
|
|
|
|||
DS:0000 |
|
Глобальні і статичні дані |
|||
|
(<64К) |
||||
|
|
|
|
|
|
|
|
|
|
|
|
CS(k):0000 |
|
Код програмного файла k |
|
<64К Сегменти |
|
|
|
|
|
|
<64К програмних |
|
|
|
|
|
|
|
|
|
|
< 64 К кодів |
|
|
|
Код програмного файла 1 |
|
|
|
|
|
|
|
|
|
CS(1):0000
Рис. 2. Схема розподілу оперативної пам'яті в моделі Large
13/37
У старших моделях пам'яті для даних і коду програми виділяється декілька сегментів оперативної пам'яті та використовується зовнішня динамічна пам'ять. Тому в цих моделях адреса зберігається в чотирибайтовому форматі: два старші байти задають сегмент, а два молодші - зміщення адреси.
Вказівники старших моделей називають довгими (чи дальніми) або far- вказівниками. Коли відбувається звертання до об'єкта через far-вказівник, сегмент адреси записується у відповідний сегментний регістр для формування фізичної адреси цього об'єкта.
Довгі вказівники дають змогу звернутись до кожного байта оперативної пам'яті в межах перших 1 Мбайт.
Формат вказівника, встановлений автоматично за заданою моделлю пам'яті, можна змінити, використовуючи модифікатори near, far або huge (всі три є зарезервованими словами Borland C). Модифікатор формату записують після базового типу вказівника перед символом *, наприклад:
char far* psymb;
14/37
near-, farта huge-вказівники.
Вказівники з модифікатором near є 16-розрядними беззнаковими цілими числами, що зберігають тільки зміщення адреси, їх доцільно застосовувати в програмах, сумарний обсяг даних яких не перевищує обсягу сегмента (64 Кбайт).
Застосування коротких вказівників у багатосегментних програмах вимагає перевизначення сегментного регістра DS або використання спеціальних форм near-вказівників, про які мова йтиме далі.
Вказівники з модифікатором far 32-розрядні, вони зберігають повну адресу об'єкта, тобто сегмент і зміщення. Такі вказівники застосовують у молодших моделях пам'яті, коли потрібно звернутись до об'єктів, розташованих за межами сегмента коду програми чи сегмента даних. Зокрема, far-вказівники використовують для роботи з даними у зовнішній динамічній пам'яті, для безпосереднього звертання до відеопам'яті, для доступу до адресного простору
BIOS тощо.
Наведемо приклад функції, яка дає змогу записати заданий символ в останню позицію екрана (правий нижній кут). Нагадаємо, що в разі заповнення цієї позиції будь-якою стандартною функцією виведення даних, відбувається автоматичний скролінг екранного зображення - зсув усіх рядків на один угору. Тому функція заповнення останнього знакомісця має важливе практичне значення.
15/37
/* Функція виведення символа в позиції екрана (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);
16/37
Два перших макроси 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).
Іншим способом формування довгих адрес та виділення їх складових частин є використання структур і об'єднань. Такий підхід дає змогу одночасно виконувати перетворення базового типу вказівника, тобто змінювати форму доступу до даних.
17/37
Розглянемо два наступних оголошення:
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.
18/37
Наприклад, наступні присвоєння:
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-вказівників пов'язане з певними проблемами, про які необхідно пам'ятати, розробляючи програми з цими вказівниками.
19/37
По-перше, одну і ту ж фізичну адресу можна задати різними значеннями вказівника. Обидва оголошені вказівники:
unsigned far* adr1= (unsigned far*)0x00000410; unsigned far* adr2= (unsigned far*)0x00410000;
задають спільну адресу початку області даних відеопараметрів BIOS. Проте в разі порівняння цих вказівників: adr1==adr2 - результат буде хибним, бо порівнюються числові значення, записані в adr1 та adr2. А в разі порівняння far- вказівників операціями <, <=, >, >= перевіряються тільки зміщення, тобто молодші половини адрес.
Другою проблемою є міжсегментний перехід. Якщо до адреси far- вказівника додати певне число, то змінюється тільки зміщення адреси, а перенесення в сегментну половину не відбувається.
Вказаних недоліків не мають вказівники з модифікатором huge. В операціях з цими вказівниками беруть участь усі 32 розряди їхніх значень.
Після кожної операції виконується нормалізація вказівника: значення сегмента коректується так, щоб значення зміщення не перевищувало 15.
20/37
