Процедури і функції в асемблері

Процедури і функції в асемблері

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

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

Для опису послідовності команд у вигляді процедури в мові асемблера використовуються дві директиви: PROC і ENDP.

Синтаксис опису процедури:

ІмяПроцедури PROC мову відстань
; тіло процедури
ІмяПроцедури ENDP

У заголовку процедури (директиві PROC) обов'язковим є тільки завдання імені процедури. Атрибут відстань може приймати значення near або far і характеризує можливість звернення до процедури з іншого сегмента коду. За замовчуванням атрибут відстань приймає значення near. і саме це значення використовується при виборі плоскою моделі пам'яті FLAT.

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

  • на початку програми (до першої виконуваної команди);
  • в кінці (після команди, яка повертає керування операційній системі);
  • проміжний варіант - тіло процедури розташовується всередині іншої процедури або основної програми;
  • в іншому модулі.

Розміщення процедури на початку сегмента коду передбачає, що послідовність команд, обмежена парою директив PROC і ENDP. буде розміщена до мітки, що позначає першу команду, з якої починається виконання програми. Ця мітка повинна бути вказана як параметр директиви END. позначає кінець програми:


.code
myproc proc near
ret
myproc endp
start proc
call myproc
.
start endp
end start

Розміщення процедури в кінці програми передбачає, що послідовність команд, обмежена директивами PROC і ENDP. буде розміщена після команди, яка повертає керування операційній системі.


.code
start proc
call myproc
.
start endp
myproc proc near
ret
myproc endp
end start

Проміжний варіант розташування тіла процедури передбачає її розміщення всередині іншої процедури або основної програми. В цьому випадку потрібно передбачити обхід тіла процедури, обмеженого директивами PROC і ENDP. за допомогою команди безумовного переходу jmp:


.code
start proc
jmp ml
myproc proc near
ret
myproc endp
ml:
.
start endp
end start

Останній варіант розташування описів процедур - в окремому модулі - припускає, що часто використовуються процедури виносяться в окремий файл. Файл з процедурами повинен бути оформлений як звичайний вихідний файл і підданий трансляції для отримання об'єктного коду. Згодом цей об'єктний файл на етапі компонування об'єднується з файлом, в якому ці процедури використовуються. Цей спосіб передбачає наявність у вихідному тексті програми ще деяких елементів, пов'язаних з особливостями реалізації концепції модульного програмування в мові асемблера. Варіант розташування процедур в окремому модулі використовується також при побудові Windows-додатків на основі виклику API-функцій.

Оскільки ім'я процедури має ті ж атрибутами, що і мітка в команді переходу, то звернутися до процедури можна за допомогою будь-якої команди умовного або безумовного переходу. Але завдяки спеціальному механізму виклику процедур можна зберегти інформацію про контекст програми в точці виклику процедури. Під контекстом розуміється інформація про стан програми в точці виклику процедури. В системі команд мікропроцесора є дві команди, які здійснюють роботу з контекстом. Це команди call і ret:

  • call ІмяПроцедури @num - виклик процедури (підпрограми).
  • ret число - повернення управління викликає програмі.

число - необов'язковий параметр, що позначає кількість байт, що вилучається з стека при поверненні з процедури.

@num - кількість байт, яке займають в стеці передані аргументи для процедури (параметр є особливістю використання транслятора MASM).

Об'єднання процедур, розташованих в різних модулях

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

Тут ім'я - ідентифікатор, визначений в іншому модулі. В якості ідентифікатора можуть виступати:

  • імена змінних;
  • імена процедур;
  • імена констант.
  • якщо ім'я - це ім'я змінної, то тип може набувати значень byte. word. dword. qword і tbyte;
  • якщо ім'я - це ім'я процедури, то тип може набувати значень near або far; в компіляторі MASM після імені процедури необхідно вказувати число байтів в стеці, які займають аргументи функції:

extern p1 @ 0: near

  • якщо ім'я - це ім'я константи, то тип повинен бути abs.
  • Приклад використання директив extern і public для двох модулів

    ; Модуль 1
    .586
    . model flat, stdcall
    . data
    extern p1 @ 0: near
    .code
    start proc
    call p1 @ 0
    ret
    start endp
    end start

    Схожі статті