Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

гос / sp-lect (1)

.pdf
Скачиваний:
19
Добавлен:
16.02.2016
Размер:
2.59 Mб
Скачать

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

в) обмеження на розмір параметрів, що можуть передаватися за значенням, повязане з розрядністю регістрів;

г) абсолютна апаратна залежність виклику.

Приклад 5.1 Приклад демонструє програмування з використанням власних підпрограм, аналогічних за своїми функціональними можливостями стандартним функціям мови С/С++.

.486 ;для bswap

.model flat, stdcall

option casemap :none

.data

 

src

db "0123456789",0

.data?

 

dst

db 1+lengthof src dup (?)

.code

 

strlen

proc ;size_t strlen(const char* String);

;Вхід:

EDI – адреса рядка

;Вихід:

EAX – довжина рядка

 

push ecx

 

push edi

 

mov ecx,-1

 

xor al,al

 

repnz scasb

 

neg ecx

 

lea eax,[ecx-2]

 

pop edi

 

pop ecx

 

ret

strlen endp

memcpy proc ;void *memcpy(void *dst, const void *src, size_t n)

;Вхід:

EDI –

адреса рядка приймача

;

ESI –

адреса рядка джерела

;

ECX – кількість

;Вихід:

немає

 

 

push esi

 

push edi

 

push ecx

 

push ecx

 

shr ecx,2

 

rep movsd

 

pop ecx

 

and ecx,3

 

rep movsb

 

pop ecx

 

pop edi

 

pop esi

 

ret

 

memcpy

endp

 

memset

proc ;void *memset( void *dest, int c, size_t count );

;Вхід: EDI –

адреса рядка, AL – символ, ECX – кількість

;Вихід:

немає

 

push edi push ebx mov ah,al mov bx,ax bswap eax mov ax,bx mov ebx,ecx shr ecx,2 rep stosd mov ecx,ebx

and ecx,3 rep stosb pop ebx pop edi ret

memset endp start: lea edi,src call strlen

mov ecx,eax lea esi,src lea edi,dst call memcpy

mov al,-1 ; EDI не змінилося

mov ecx,lengthof dst call memset

ret end start

5.2.2 Передавання параметрів через стек

При передаванні параметрів через стек процесору код, що передує виклику підпрограми розміщує параметри в стеку в наперед визначеному порядку і виконує виклик за допомогою команди CALL.

Команда CALL розміщує на верхівці стеку услід за параметрами інформацію, яка використовується для повернення з підпрограми. При ближньому переході на верхівці стеку розміщується адреса наступної за CALL команди, тобто значення, яке при поверненні із підпрограми командою RET стане значенням регістру RIP|EIP.

Для доступу до параметрів необхідно «пірнути» в глибину стеку не знімаючи з верхівки адресу повернення. Для цього, зазвичай, використовується базова адресація зі зміщенням з базою в регістрі RBP|EBP (нагадаємо, що при використанні EBP у якості базового регістру, у якості сегментної складової за

замовченням використовується регістр SS, тобто виконується звернення до стеку).

Після збереження в стеку за допомогою PUSH RBP|EBP значення RBP|EBP яке буде змінюватися, і копіювання адреси верхівки стеку за допомогою MOV RBP|EBP , RSP|ESP залишається лише відрахувати зміщення від RBP|EBP до розміщених в стеку параметрів підпрограми.

Після фіксації стану стеку в RBP|EBP, виконаної розглянутими вище двома командами, стає можливим використання команд для роботи зі стеком, які змінюють RSP|ESP. Після фіксації стану стеку параметри зі стеку можна забирати коли в цьому виникне потреба. При використанні в підпрограмі команд для роботи зі стеком необхідно забезпечити його стан перед виконанням команди RET таким, яким він був при вході в підпрограму.

Після повернення з підпрограми виконанням команди RET в стеку залишаються параметри, які підпрограма отримувала при виклику. Вирівнювання стеку в той самий стан, який він мав до виклику підпрограми може бути виконано як в коді, що викликав підпрограму, так і безпосередньо у коді самої підпрограми.

Вирівнювання стеку в коді, що викликав підпрограму виконується за допомогою команди ADD RSP|ESP,N після команди CALL. Для вирівнювання стеку безпосередньо у коді самої підпрограми достатньо завершити підпрограму командою RET N. В обох випадках число N дорівнює загальному розміру в байтах усіх параметрів підпрограми в стеку. Зважаючи на те, що система команд процесору не дозволяє заносити в стек байт, число N завжди парне.

Передавання параметрів через стек процесору має наступні переваги:

а) знижує апаратну залежність виклику і спрощує реалізацію компіляторів мов високого рівня;

б) знижує (практично знімає) обмеження на кількість параметрів;

в) знімає обмеження на розмір параметрів, що можуть передаватися за значенням;

г) знижує привязку коду підпрограми до регістрів, бо параметри зі стеку можна забирати коли в цьому виникне потреба.

Передавання параметрів через стек має і певні недоліки:

а) уповільнює код виклику через необхідність попереднього розміщення параметрів в стеку, який є частиною памяті;

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

Приклад 5.2 Цей приклад відрізняється від попереднього (Приклад 5.1) лише іншою реалізацією передавання параметрів в підпрограми через стек.

.486 ;для bswap

.model flat, stdcall

option casemap :none

.data

 

src

db "0123456789",0

.data?

 

dst

db 1+lengthof src dup (?)

.code

 

strlen

proc ;size_t strlen(const char* String)

;Вхід:

адреса рядка в стеку,

;Вихід:

EAX – довжина рядка

 

push ecx

 

push edi

 

mov edi,[esp+12]

 

mov ecx,-1

 

xor al,al

 

repnz scasb

 

neg ecx

 

lea eax,[ecx-2]

 

pop edi

 

 

pop ecx

 

 

ret 4

 

strlen

endp

 

memcpy

proc ;void *memcpy(void *dst, const void *src, size_t n)

 

push ebp

 

 

mov ebp,esp

 

push edi

 

 

push esi

 

 

mov edi,[ebp+8]

 

mov esi,[ebp+12]

 

mov ecx,[ebp+16]

 

shr ecx,2

 

 

rep movsd

 

 

mov ecx,[ebp+16]

 

and ecx,3

 

 

rep movsb

 

 

pop ebp

 

 

pop esi

 

 

pop edi

 

 

ret

 

memcpy

endp

 

memset

proc

;void *memset(void *dest, int c, size_t count );

 

push ebp

 

mov ebp,esp push edi push ebx

mov edi,[ebp+14] mov al,[ebp+12] mov ecx,[ebp+8] mov ah,al

mov bx,ax bswap eax

mov ax,bx mov ebx,ecx

shr ecx,2 rep stosd and ecx,3 rep stosb pop ebx pop edi pop ebp ret 10

memset endp

start: push offset src

 

call strlen

 

push eax

; в EAX довжина рядка від strlen

push offset src

 

push offset dst

 

call memcpy

 

add esp,12

 

push offset dst mov al,-1 movzx ax,al push ax

push lengthof dst call memset

ret end start

6 МАКРОЗАСОБИ MICROSOFT MACRO ASSEMBLER

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

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

Макрозасоби це певний набір сервісних послуг асемблера, який реалізується програмними засобами транслятора. Для асемблерів які підтримують макрозасоби транслятор складається з двох частин препроцесора (макроасемблера) і власне транслятора (асемблера).

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

Набір макрозасобів (мова препроцесора) різниться для різних пакетів асемблерів, хоча існує певна їх частина, яка підтримується більшістю пакетів в незмінному синтаксисі. Мабуть найбільшу кількість власних директив і операторів має препроцесор FASM, однак не дуже відстає від нього і MASM, повна назва якого Microsoft Macro Assembler підкреслює його орієнтованість на використання макрозасобів і директив компіляції високо рівня.

Макрозасоби MASM можна розділити на декілька основних груп

Рисунок 6.1

Кожна наступна група макрозасобів зображених на рисунку в порядку зліва направо розширюватиме наші можливості щодо макропрограмування.

6.1 Текстові макро

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

name TEXTEQU <Текст>

name TEXTEQU %Константний вираз

name TEXTEQU Інший текстовий макро

name TEXTEQU Макрофункція

Символи «<», «>», «%» є макро операторами препроцесора. «Лапки» з символів < > визначають літерали, вказуючи препроцесору, що він повинен сприймати щось укладене у них як єдиний рядок символів, навіть якщо він містить коми, пробіли та будь-які інші символи. При цьому самі символи «<» і «>» в рядок не потрапляють.

Якщо права частина текстового макро містить символи «<», «>», «,» «"», «'», «%», «;» які можуть мати функціональність в тому, чи іншому вираженні їх слід екранувати (затінити, відмінити) за допомогою макрооператора «!», який повідомляє транслятору про те, що наступний за ним символ слід уважати просто символом, навіть якщо це кома, кутова дужка або щось інше.

В константних арифметичних виразах можуть використовуватися оператори LENGTH, SIZE, WIDTH, MASK, LENGTHOF, SIZEOF, унарні «+» і «-», знаки бінарних операцій «+»,«-», «*», «/», MOD, оператори побітового зрушення SHL, SHR, логічні побітові операції NOT, AND, OR, XOR, дужки «(»

та «)».

Операндами арифметичних виразів можуть виступати числа, попередньо визначені макрозмінні, макропараметри. В арифметичних виразах не повинно

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

Макрооперанд «%» використовується для того, щоб повідомити препроцесору про те, що весь наступний за ним текст являє собою константний вираз або текстовий макро, який потрібно попередньо обчислити або розгорнути, і надалі використовувати результат у текстовому вигляді.

6.2 Макровизначення

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

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

name MACRO

Макровизначення

ENDM

Макровизначення розміщують на початку вихідного тексту програми або в окремому файлі. При розміщенні тексту макровизначення в окремому файлі на початку програми необхідно задати директиву INCLUDE [[диск:\]шлях\]ім'я файла. Зустрівши директиву INCLUDE, препроцесор знайде зазначений файл і вставить увесь його текст до технологічного лістингу програми, після чого продовжить подальше оброблення. Таким чином, директиву INCLUDE можна вважати певним розширенням текстових макро. За допомогою INCLUDE до

Соседние файлы в папке гос