Засоби системного програмування
Практична робота № 6
МОДУЛЬНЕ ПРОГРАМУВАННЯ
Створюючи програмне забезпечення на мові Асемблера проектувальник потенційно може зіткнутись з такими ситуаціями:
необхідність компонування декількох програм написаних на різних мовах програмування (наприклад, для об’єднання потужності мов високого рівня та ефективності асемблера);
програма, написана у вигляді одного модуля, може виявитись завеликою для асемблювання;
окремі модулі можуть бути написані різними проектувальниками з метою подальшої інтеграції;
з причини великого розміру
EXTRN SUBPROG:FAR
MAINPROG: .
.
CALL SUBPROG
.
.
PUBLIC SUBPROG
SUBPROG: .
.
.
RET
Директива EXTRN має такий формат:
EXTRN ім’я : тип [, … ]
Можна призначити більше одного імені (до кінця рядка) або закодувати додаткові директиви EXTRN. В іншому асемблерному модулі відповідне ім’я повинно бути визначене і ідентифіковане як PUBLIC. Існують такі типи елементів: ABS, BYTE, WORD, DWORD, FAR, NEAR. Ім’я може бути визначене через EQU і повинно задовольняти реальному визначенню імені.
Директива PUBLIC вказує асемблеру і компонувальнику, що адреса вказаного ідентифікатора є доступною з інших програм. Директива має такий формат:
PUBLIC ідентифікатор [, … ]
Можна призначити більше одного ідентифікатора (до кінця рядка) або закодувати додаткові директиви PUBLIC. Ідентифікатори можуть бути мітками (включаючи PROC-мітки), змінними або числами. Неправильними ідентифікаторами є імена регістрів та EQU-ідентифікатори, що призначають більш як двобайтні значення.
Існує декілька способів компонування програм. Кожен з них характеризується одною або декількома такими рисами:
директиви EXTRN і PUBLIC в якості міток;
спільний кодовий сегмент - шляхом використання директиви PUBLIC в заголовку кодового сегменту кожної з програм, що компонуються;
спільні дані.
Використання директив EXTRN і PUBLIC в якості міток
Програма на рисунку нижче складається з основної програми MAINPROG та підпрограми SUBPROG. Директива EXTRN в основній програмі визначає SUBMUL як точку входу в підпрограму. Підпрограма містить директиву PUBLIC, яка вказує компонувальнику на те, що точкою входу для виконання є мітка SUBMUL. Підпрограма перемножує вміст регістру AX (значення з поля даних PRICE) на вміст регістру BX (значення з поля даних QTY), при цьому результат операції буде розміщено в регістровій парі DX:AX (в даному випадку – 002E 4000). В підпрограмі відсутній сегмент даних, оскільки вона не визначає ніяких даних.
*Примітка: Для успішного компонування компонувальник потребує знайти хоча б один стековий сегмент. В підпрограмі не визначено стекового сегменту, оскільки вона використовує ті ж самі стекові адреси, що й основна програма. Таким чином, стек, що визначається в основній програмі, є доступним і в підпрограмі.
Використання спільного кодового сегменту
В основній програмі та в усіх підпрограмах, що компонуються разом, необхідно використати атрибут PUBLIC в директиві SEGMENT:
CODESG SEGMENT PARA PUBLIC 'CODE'
В даному випадку з таблиці ідентифікаторів слідує, що узагальнений тип кодового сегменту – PUBLIC. Необхідно зауважити, що карта компонування вказує на наявність в програмі лише одного кодового сегменту - внаслідок того, що заголовки кодових сегментів основної програми та підпрограми співпадають, компонувальник об’єднує їх в один фізичний кодовий сегмент.
Використання спільних даних
Наявність спільних даних уможливлює обробку в одному асемблерному модулі даних, що є визначеними в іншому асемблерному модулі.
Для того, щоби змінні PRICE та QTY визначались в основній програмі, але завантаження значень з цих областей пам’яті в регістри BX та AX виконувалась в підпрограмі, необхідно виконати такі зміни в програмах з попереднього пункту:
в основній програмі ідентифікатори PRICE та QTY визначені як PUBLIC. Сегмент даних так само визначений як PUBLIC;
в підпрограмі ідентифікатори PRICE та QTY визначені як EXTRN та WORD. Таке визначення вказує асемблеру на довжину цих полів в 2 байт.
*Примітка:
Основна програма та підпрограма можуть визначати будь-які інші елементи даних, але спільними елементами будуть лише ті, що мають атрибути PUBLIC та EXTRN.
При компонуванні двох і більше асемблерних модулів необхідно визначити стек достатньо великого розміру – на практиці для великих програм визначення розміру в 64 слова є достатнім.
Передача параметрів
Даний спосіб взаємодії програм характеризується тим, що програма, що викликає іншу, передає їй параметри шляхом запису даних до стеку. Кожна команда PUSH при цьому повинна записувати до стеку дані розміром в одне слово з пам’яті або з регістру.
Програма, приведена нижче, перш ніж викликати підпрограму SUBMUL заносить у стек значення з полів PRICE і QTY. Після команди CALL стек виглядає в так:
... │ 1600 │ D213 │ 4001 │ 0025 │ 0000 │ C213 │
6 5 4 3 2 1
Ініціалізуюча команда PUSH DS заносить адресу сегмента у стек. Ця адреса може відрізнятися в різних версіях DOS.
Команда PUSH AX заносить у стек нульова адреса.
Команда PUSH PRICE заносить у стек слово (2500).
Команда PUSH QTY заносить у стек слово (0140).
Команда CALL заносить у стек уміст регістра CS (D213)
Оскільки що команда CALL представляє тут міжсегментний виклик, то в стек заноситься також уміст регістра IP(1600).
Програма, що викликається використовує регістр BP для доступу до параметрів у стеку, але колись вона запам'ятовує вміст регістра BP, записуючи його в стек. У даному випадку, припустимо, що регістр BP містить нуль, тоді нульове слово буде записано у вершині стека (ліворуч). Потім програма поміщає в регістр BP уміст із регістра SP, тому що як індексний регістр може використовуватися регістр BP, але не SP. Команда завантажує в регістр BP значення 0072. Спочатку регістр SP містив розмір порожнього стека, тобто комірка 80. Запис кожного слова в стек зменшує вміст SP на 2:
│ 0000 │ 1600 │ D213 │ 4001 │ 0025 │ 0000 │C213 │
│ │ │ │ │ │ │
SP: 72 74 76 78 7A 7C 7E
Оскільки BP тепер також містить 0072, то параметр ціни (PRICE) буде за адресою BP+8, а параметр кількості (QTY) - за адресою BP+6. Програма пересилає ці величини зі стеку в регістри AX і BX відповідно і виконує множення.
Перед поверненням у вихідну програму в регістрі BP відновлюється початкове значення, а вміст у регістрі SP збільшується на 2, з 72 до 74.
Остання команда RET являє собою "довгий" повернення у вихідну програму. По цій команді виконуються наступні дії:
З вершини стека відновлюється значення регістра IP (1600).
Вміст регістра SP збільшується на 2, від 74 до 76.
З нової вершини стеку відновлюється значення регістра CS (D213).
Вміст регістра SP збільшується на 2 від 76 до 78.
У такий спосіб здійснюється коректне повернення у викликаючу програму. Залишилося одне невелике пояснення. Команда RET закодована як RET 4. Параметр 4 являє собою кількість байт у стеці використаних при передачі параметрів (два слова в даному випадку). Команда RET додасть цей параметр до вмісту регістра SP, одержавши значення 7C. Таким чином, зі стека виключаються непотрібні більше параметри. Будьте особливо уважні при відновленні регістра SP - помилки можуть привести до не передбачуваного результату.