Лекція №17.
Означення 16 та 32-х бітових програм. Сумісне використання кодових сегментів. Перенаправлення керування з кодових сегментів фіксованого розміру.
Для розрізняння та підтримки 16- і 32-бітових сегментів використовуються наступні механізми:
Прапорець D в дескрипторах сегменту коду.
Прапорець B у дескрипторах сегменту стеку.
16- і 32-бітові виклики gates, interrupt gates I trap gates.
Префікси розміру операнду та розміру адреси.
16- і 32-бітові регістри загального призначення
Прапорець D в дескрипторі сегменту кода визначає розмір операнду і розмір адреси інструкції сегменту коду по замовчуванню. Сегмент коду з встановленим прапорцем D є 32-бітним сегментом, інакше – 16-бітним.
Прапорець B в дескрипторі сегменту стеку вказує розмір покажчика стеку.
При передачі програмою керування іншому сегменту коду через виклик gate, interrupt gate, trap gate розмір операнда, що використовується при передачі визначається типом gate. Тип gate визначає, яким чином інформація, що повертається записується в стек.
Для більш ефективної і безпечної роботи процесора, 32-бітні програми повинні мати прапорець D в дескрипторі сегмента коду і прапорець B в дескрипторі сегменту стека.
Префікси інструкцій повинні використовуватись для перевизначення розміру операнда і адреси по замовчуванню. Ці префікси повинні використовуватись в режимі реальної адресації так само, як і в захищеному режимі і режимі virtual-8086.
Змішування 32- і 16-бітних операцій в одному сегменті дозволяється при використанні наступних префіксів інструкцій:
The operand-size prefix (66H)
The address-size prefix (67H)
Ці префікси змінюють розмір по замовчуванню на зворотній.
В 32-бітовому сегменті коду:
Переміщує 32 біти з 32-бітового регістра в пам’ять з використанням 32-бітової ефективної адреси
При префіксі розміру операнда, переміщує 16 біт з 16-бітового регістру в пам’ять з використанням 32-бітової ефективної адреси.
При префіксі розміру адреси, переміщує 32 біт з 32-бітового регістру в пам’ять з використанням 16-бітової ефективної адреси.
При префіксах розміру операнда і розміру адреси переміщує 16 біт з 16-бітного регістру в пам’ять з використанням 16-бітової ефективної адреси.
В 16-бітовому сегменті коду:
Переміщує 16 біт з 16-бітного регістру в пам’ять з використанням 16-бітової ефективної адреси.
При префіксі розміру операнда, переміщує 32 біти з 32-бітового регістру в пам’ять з використанням 16-бітової ефективної адреси.
При префіксі розміру адреси, переміщує 16 біт з 16-бітового регістру в пам’ять з використанням 32-бітової ефективної адреси.
При префіксах розміру операнда і розміру адреси переміщує 32 біт з 32-бітного регістру в пам’ять з використанням 32-бітової ефективної адреси.
Ці приклади показують, що будь-яка інструкція може генерувати будь-яку комбінацію розмарів операндів і адрес. Вибір 16- і 32-бітового розміру для сегменту кода звичайно базується на наступних кретеріях:
Швидкодія – завжди 32-бітний сегмент коду, якщо можливо.
Операційна система, на якій буде запускатись сегмент коду – якщо операційна система 16-бітна, вона може підтримувати 32-бітні програмні модулі.
Режим роботи – якщо сегмент коду створюється для запуску в режимі реальної адресації, режимі virtual-8086 або SMM, він повинен бути 16-бітовим сегментом коду.
Зворотня сумісність з ранніми версіями процесорів родини Intel – якщо сегмент коду повинен запускатись на процесорах Intel 8086 або Intel 286, він повинен бути 16-бітовим.

Доступ до сегменту даних може бути здійснений як з 16-, так і 32-бітових сегментів коду. Якщо сегмент даних більший за 64К, він розподіляється між 16- і 32-бітними сегментами коду; дані, до можна доступитись з 16-бітного сегменту коду повинні бути розміщені у перших 64К сегменту даних.
Стек, який має менше 64К може бути розподілений як між 16-, так і між 32-бітовими сегментами коду.
Для виконання безпечного виклику 32-бітового коду в 16-бітовому сегменті коду існує 3 шляхи:
Виконання виклику через 32-бітові gate.
Виконання 16-бітових викликів 32-бітових інтерфейсних процедур. Інтерфейсні процедури виконують 32-бітові виклики портібних процедур.
Модифікація 16-бітової процедури – додавання префіксу розміру операнду перед викликом.
Для виконання безпечного виклику 16-бітового коду в 32-бітовому сегменті коду,аналогічно,існують 3 шляхи:
Виконання виклику через 16-бітові gate.
Виконання 32-бітових викликів 16-бітових інтерфейсних процедур. Інтерфейсні процедури виконують 16-бітові виклики портібних процедур.
Модифікація 32-бітової процедури – додавання префіксу розміру операнду перед викликом.
Для інструкцій передавання управління, що використовують покажчик для ідентифікації наступної інструкції, розмір атрибуту розміру операнду визначає розмір зміщення покажчика. Наслідки з даного правила наступні:
Виклики інструкцій JMP, CALL, RET з 32-бітового сегменту в 16-бітовий сегмент завжди можливий з використанням розміру операнду 32 біти.
Виклики інструкцій JMP, CALL, RET з 16-бітового сегменту в 32-бітовий сегмент не можуть адресувати більше FFFFH.
Оскільки стек керується по-різному з 16-бітових і 32-бітових викликах, атрибут розміру операнду інструкції RET повинен відповідати інструкції CALL.
EMBED PBrush
Якщо при виконанні 32-бітового коду виконується виклик 16-бітового сегменту коду, що має подібний або вищий рівень привілеїв через 16-бітовий gate, верхні 16 біт регістру ESP не будуть досяжні при поверненні в 32-бітовий сегмент коду(після виконання інструкції RET в 16-бітовому сегменті коду.
При виконанні інструкції CALL і відповідної інструкції RET в сегменті коду, що має прапорець D з одинаковими значеннями, використовуються установки по замовчуванню. При виконанні інструкцій CALL і відповідно RET в сегментах з різними значеннями прапорці D, викоритовується префікс розміру операнду.
Для визначення розміру операнду при виклику використовуються наступні дані:
Прапорець D в дескрипторі сегменту для виклику сегменту коду.
Префікс розміру операнду інструкції.
Тип gate(16- або 32-бітовий) – якщо виклик робиться через gate.
Коли виклик робиться за допомогою покажчика(а не через call gate), прапорець D для викликаючого сегменту коду визначає розмір операнда для інструкції CALL. Цей атрибут розміру операнда може бути перевизначеним використанням префіксу розміру операнда в інструкції CALL. Якщо інструкція CALL посилається на дескриптор call gate, тип виклику визначається типом call gate. Зміщення точки виклику в сегменті коду береться з дескриптора gate; але, якщо використовується 32-бітовий call gate, процедура в 16-бітовому сегменті коду може викликати процедуру, що розміщена по адресі, більшій ніж 64К, рахуючи від початку 32-бітового сегменту коду, оскільки 32-бітові call gate використовують 32-бітне зміщення.
Немодифіковані 16-бітні сегменти коду можуть успішно запускатись на 8086 процесорах або в режимі реальної адресації на більш пізніх версіях процесорів родини Intel, при обнуленому прапорці D і не використовувати префіксів перевизначення розміру операнду. Як результат, всі інструкції CALL в цьому сегменті коду будуть використовувати 16-бітовий атрибут розміру операнда.
При посиланні на 32-бітові gates у 16-бітових процедурах важливою є кількість параметрів при кожному вивлику процедури. Поле підрахунку у дескрипторі gate визначає розмір рядка параметрів для копіювання з біжучого стеку в стек більш привілейовної процедури. Поле підрахунку в 16-бітовому дескрипторі gate визначає кількість 16-бітових слів, що повинні бути скопійовані; аналогічно, в 32-бітовому дескрипторі gate.
Передавання управління програмою, викликане виключенням або перериванням завжди виконується через gate переривання або пастки. Тому, тип цього gate(16- або 32-бітовий) визначається атрибутом розміру операнду, що використовується при виклиці обробника переривання ібо виключення в іншому сегменті коду.
32-бітний gate переривання або пастки забезпечує безпечний інтерфейс для 32-бітових обробників переривань або виключень якщо вони відбуваються і в 32-бітових, і в 16-бітових сегментах коду.
Якщо зміщення або покажчик передаються як параметр між 16-бітовою і 32-бітовою процедурами, необхідна певна обробка. Якщо 32-бітова процедура передає покажчик на дані, що розміщені поза межами 64К в 16-бітову процедуру, 16-бітова процедура не зможе їх використовувати. За вийнятком цього обмеження, інтерфейсний код може використовувати будь-яке перетворення форматів між 32- і 16-бітовими покажчиками.
Написання інтерфейсного коду між 32- і 16-бітовими процедурами може розв’язати наступні інтерфейсні проблеми:
Дозвіл процедурам в 16-бітовому сегменті коду викликати процедури, що знаходяться за зміщенням, більшим від FFFFH в 32-бітовому сегменті коду.
Відповідність атрибутів розміру операнду при роботі інструкцій CALL і  RET.
Передавання параметрів (даних), включаючи рядки управляючих параметрів зі змінною кількістю 16-бітових слів.
Можливе спотворення верхніх бітів регістру ESP.
Інтерфейсна процедура спрощується, якщо виконуються наступні правила:
Інтерфейсна процедура повинна розміщуватись в 32-бітовому сегменті коду(прапорець D для дескриптора сегменту коду повинен бути встановлений)
Всі процедури, що можуть біти викликані 16-бітовими процедурами повинні мати зміщення не більше від FFFFH.
Всі адреси, що зберігаються 16-бітовими процедурами повинні мати зміщення не більше від FFFFH.
Інтерфейсні процудури ускладнюються, якщо хоча б одне з цих правил не виконується. Структура інтерфейсної процедури залежить від типів викликів, що вона повинна підтримувати:
Виклики 32-бітових процедур з 16-бітових.
Виклики 16-бітових процедур з 32-бітових.