Міністерство освіти і науки України
Національний університет “Львівська політехніка”
/
Лабораторна робота № 1
Тема:
“Особливості програмування з використанням 32-розрядного Асемблера”
Львів 2015
Мета: Ознайомитись з програмною моделлю 32-розрядних мікропроцесорів Intel та оволодіти навиками створення програм, використовуючи 32-розрядний Асемблер.
ТЕОРЕТИЧНІ ВІДОМОСТІ
Основою для розробки низькорівневого системного програмного забезпечення є програмна модель комп’ютера, частиною якої є програмна модель мікропроцесора. До складу програмної моделі мікропроцесорів Intel сімейства x86 входять 32 регістри в тій чи іншій мірі доступні для використання програмістом. Дані регістри можна розділити на дві великі групи:
16 регістрів користувача;
16 системних регістрів.
програмах на мові асемблера регістри використовуються дуже інтенсивно. Більшість регістрів мають певне функціональне призначення.
Регістри користувача
Як випливає з назви, призначеними для користувача регістри називаються тому, що програміст може використовувати їх при написанні своїх програм. До цих регістрів відносяться (рис.1.1):
вісім 32-бітових регістрів, які можуть використовуватися програмістами для зберігання даних і адрес (їх ще називають регістрами загального призначення (РЗП)):
eax/ax/ah/al; ebx/bx/bh/bl; edx/dx/dh/dl; ecx/cx/ch/cl; ebp/bp;
esi/si; edi/di; esp/sp.
шість сегментних регістрів: cs, ds, ss, es, fs, gs;
Pегістри загального призначення
eax
ax
ah
al
31
15
7
0
ebx
bx
bh
bl
31
15
7
0
ecx
cx
ch
cl
31
15
7
0
ebx
bx
bh
bl
31
15
7
0
ebp
bp
31
15
0
esi
si
31
15
0
edi
di
31
15
0
esp
sp
31
15
0
Pегістри управління та стану
eflags
flags
31
15
0
eip
ip
31
15
0
Сегментні регістри
cs
15
0
ss
15
0
ds
15
0
es
15
0
ts
15
0
qs
15
0
Рис. 1.1. Регістри користувача мікропроцесорів i486 і Pentium
Як видно з рис. 1.1, частина регістрів зображені розділено. Це не різні регістри — це частини одного великого 32-розрядного регістру. Їх можна використовувати в програмі як окремі об’єкти. Так зроблено, для забезпечення сумісності з програмами, написаними для ранніх, 16-розрядних, моделей мікропроцесорів фірми Intel, починаючи з i8086. Мікропроцесори i486 і Pentium мають в основному 32-розрядні регістри. Їх кількість, за винятком
сегментних регістрів, така ж, як і у i8086, але розмірність більше, що і відображено в їх позначеннях — вони мають приставку e (Extended).
Регістри загального призначення
Всі регістри цієї групи дозволяють звертатися до своїх “молодших” частин (див. рис. 1.1). Слід відзначити, що використовувати для самостійної адресації можна тільки молодші 16- і 8-бітові частини цих регістрів. Старші 16 біт цих регістрів як самостійні об’єкти програмування недоступні. До РЗП відносяться:
eax/ax/ah/al (Accumulator register) — акумулятор. Використовується для зберігання проміжних даних. У деяких командах використання цього регістра обов’язкове;
ebx/bx/bh/bl (Base register) — базовий регістр. Зазвичай застосовується для зберігання базової адреси деякого об’єкту в пам’яті;
ecx/cx/ch/cl (Count register) — регістр-лічильник. Застосовується в командах, що проводять деякі дії, що повторюються. Його використання часто неявно і приховано в алгоритмі роботи відповідної команди. Наприклад, команда організації циклу loop окрім передачі управління команді, що знаходиться за деякою адресою, аналізує і зменшує на одиницю значення регістра ecx/cx;
edx/dx/dh/dl (Data register) — регістр даних. Так само, як і регістр eax/ax/ah/al, зберігає проміжні дані. У деяких командах його використання обов’язкове, а для деяких це відбувається неявно.
Наступні два регістри використовуються для підтримки операцій, що проводять послідовну обробку ланцюжків елементів, кожний з яких може мати довжину 32, 16 або 8 біт:
esi/si (Source Index register) — індекс джерела.
edi/di (Destination Index register) — індекс приймача (одержувача).
архітектурі мікропроцесора на програмно-апаратному рівні підтримується така структура даних, як стек. Для роботи із стеком в системі команд мікропроцесора є спеціальні команди, а в програмній моделі мікропроцесора для цього існують спеціальні регістри:
esp/sp (Stack Pointer register) — регістр покажчика стеку. Містить покажчик вершини стеку в поточному сегменті стеку.
ebp/bp (Base Pointer register) — регістр покажчика бази кадру стеку.
Призначений для організації довільного доступу до даних усередині стеку.
Жорстке закріплення регістрів для деяких команд дозволяє компактніше кодувати їх машинне подання. Знання цих особливостей дозволяє при необхідності хоч би на декілька байтів заощадити пам’ять, що займається кодом програми.
Робота з масивами
Регістри загального призначення використовуються для адресації масивів. При цьому застосовується адресація за базою з масштабуванням: початкова адреса + база * масштабуючий коефіцієнт бази. Допустимі значення масштабуючого коефіцієнту бази (кількість байтів, які займає 1 елемент масиву) рівні 1, 2, 4, 8.
Таким чином, щоб записати 3-й елемент масиву оголошеного мовою С як short arr[15]
в edx треба написати код:
mov eax, arr
mov ebx, 2
mov edx,[eax+ebx*2]
Сегментні регістри
У програмній моделі мікропроцесора є шість сегментних регістрів: cs, ss, ds, es, gs та fs. Їх існування обумовлено специфікою організації і використання оперативної пам’яті мікропроцесорами Intel. Вона полягає в тому, що мікропроцесор апаратно підтримує структурну організацію програми у вигляді трьох частин, що називаються сегментами. Відповідно, така організація пам’яті називається сегментною.
Для того, щоб вказати на сегменти, до яких програма має доступ в конкретний момент часу, і призначені сегментні регістри. Фактично, з невеликою поправкою, як показано далі, в цих регістрах містяться адреси пам’яті з яких починаються відповідні сегменти. Логіка обробки машинної команди побудована так, що при вибірці команди доступу до даних програми або до стеку неявно використовуються адреси з певних сегментних регістрів. Мікропроцесор підтримує наступні типи сегментів:
Сегмент коду. Містить команди програми. Для доступу до цього сегменту служить регістр cs (code segment register) — сегментний регістр коду. Він містить адресу сегменту з машинними командами, до якого має доступ мікропроцесор (тобто ці команди завантажуються в конвейєр мікропроцесора).
Сегмент даних. Містить оброблювані програмою дані. Для доступу до цього сегменту служить регістр ds (data segment register) — сегментний регістр даних, який зберігає адресу сегменту даних поточної програми.
Сегмент стеку. Цей сегмент є ділянкою пам’яті, що називається стеком. Роботу із стеком мікропроцесор організовує за наступним принципом:
останній записаний в цю ділянку елемент вибирається першим. Для доступу до цього сегменту служить регістр ss (stack segment register) — сегментний регістр стеку, що містить адресу сегменту стеку.
Додатковий сегмент даних. Неявно алгоритми виконання більшості машинних команд припускають, що оброблювані ними дані розташовані в сегменті даних, адреса якого знаходиться в сегментному регістрі ds. Якщо програмі недостатньо одного сегменту даних, то вона має можливість використовувати ще три додаткові сегменти даних. Але на відміну від основного сегменту даних, адреса якого міститься в сегментному регістрі ds, при використанні додаткових сегментів даних їх адреси потрібно указувати явно за допомогою спеціальних префіксів перевизначення сегментів в команді. Адреси додаткових сегментів даних повинні міститися в регістрах es, gs, fs (extension data segment registers).
Регістри стану і управління
У мікропроцесор включені декілька регістрів (див. рис. 1.1), які постійно містять інформацію про стан як самого мікропроцесора, так і програми, команди якої в даний момент завантажені в конвеєр. До цих регістрів відносяться:
регістр прапорів eflags/flags;
регістр покажчика команди eip/ip.
Використовуючи ці регістри, можна одержувати інформацію про результати виконання команд і впливати на стан самого мікропроцесора. Розглянемо докладніше призначення і вміст цих регістрів.
eflags/flags (flag register) — регістр прапорів. Розрядність eflags/flags — 32/16 бітів. Окремі біти даного регістра мають певне функціональне призначення і називаються прапорцями. Молодша частина цього регістра повністю аналогічна регістру flags для i8086. На рис. 1.2 показано вміст регістра eflags.
Виходячи з особливостей використання, прапори регістра eflags/flags можна розділити на три групи:
8 прапорців стану. Ці прапорці можуть змінюватися після виконання машинних команд. Прапорці стану регістра eflags відображають особливості результату виконання арифметичних або логічних операцій. Це дає можливість аналізувати стан обчислювального процесу і реагувати на нього за допомогою команд умовних переходів і викликів підпрограм.
1 прапорець управління. Позначається df (Directory Flag). Він знаходиться в 10-му біті регістру eflags і використовується для операцій рядками даних. Значення прапорця df визначає напрям поелементної обробки в цих операціях: від початку рядка до кінця або навпаки, від кінця рядка до його початку (df = 1). Для роботи з прапорцем df існують спеціальні команди: cld (зняти прапорець df) і std (встановити прапорець df). Застосування цих команд дозволяє привести прапорець df у відповідність з алгоритмом і забезпечити автоматичне збільшення або зменшення лічильників при виконанні операцій з рядками;
5 системних прапорців, для керування вводом/виводом, маскованими перериваннями, відлагодженням, перемиканням між завданнями і віртуальним режимом 8086. Прикладним програмам не рекомендується модифікувати без необхідності ці прапорці, оскільки в більшості випадків це приведе до переривання роботи програми.
Прапорці стану:
переповнення
знаку
нуля
додаткового переносу
рівень привілеїв вводу-виводу
парності
вкладеності задач
переносу
31
…
18
17
16
15
14
13 12
11
10
09
08
07
06
05
04
03
02
01
00
0
…
AC
VM
RF
0
NT
IOPL
OF
DF
IF
TF
SF
ZF
0
AF
0
PF
0
CF
Прапорець керування:
Системні прапорці:
напряму
трасування
переривання
відновлення
віртуального режиму 8086
контролю вирівнювання
Рис. 1.2. Вміст регістра eflags
eip/ip (Instraction Pointer register) — регістр-покажчик команд. Регістр eip/ip має розрядність 32/16 бітів і містить зміщення до наступної команди, що має виконуватись, відносно адреси записаної в сегментний регістр cs. Цей регістр безпосередньо недоступний програмісту, але завантаження і зміна його значення проводяться різними командами управління, до яких відносяться команди умовних і безумовних переходів, виклику і повернення з процедур. Виникнення переривань також приводить до модифікації регістра eip/ip.
Системні регістри мікропроцесора
Сама назва цих регістрів говорить про те, що вони виконують специфічні функції в системі. Використання системних регістрів жорстко регламентовано. Саме вони забезпечують роботу захищеного режиму. Їх також можна розглядати як частина архітектури мікропроцесора, яка навмисно залишена видимою для того, щоб кваліфікований системний програміст міг виконати самі низькорівневі операції. Системні регістри можна розділити на три групи:
чотири регістри управління;
чотири регістри системних адрес;
вісім регістрів відлагодження.
Виконання лабораторної роботи:
Завдання:
Створити, використовуючи мову асемблера мікропроцесорів сімейства x86 Intel, *.exe програму, яка реалізовує обчислення, заданого варіантом виразу. A = {a[i]} – наперед заданий масив з N чисел цілих чисел. c, d – цілі константи. K, L – цілі додатні числа.
Переконатися у правильності роботи програми використовуючи VKDebug.
Скласти звіт про виконану роботу з приведенням тексту програми.
Дати відповідь на контрольні запитання.
Варіант №3:
Знайти число додатних елементів у А
Код програми:
.586
.model flat, stdcall
option casemap: none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\masm32.inc
include \masm32\include\debug.inc
include \masm32\include\user32.inc
includelib\masm32\lib\kernel32.lib
includelib\masm32\lib\masm32.lib
includelib\masm32\lib\debug.lib
.data
Arr dd 2,5,1,-2,3,4,0,-3,-5
Count dd 0
ArrSize dd 9
.code
main:
mov ecx,ArrSize
cout:
dec ecx
mov eax,[Arr+ecx*4]
PrintDec eax," "
cmp ecx,0
jg cout
mov ecx,ArrSize
Looper:
cmp ecx,0
je EXIT
dec ecx
mov eax,[Arr+ecx*4]
cmp eax,0
jg CountPP
jle Looper
CountPP:
inc Count
jmp Looper
EXIT:
PrintDec Count,"The quantity of positive nums "
Invoke ExitProcess,NULL
end main
Результат виконання програми:
/
Словесний опис алгоритму:
В ArrSize зберігається розмір масиву. Зміна Count призначена для підрахунку кількості чисел які більші за 0. В регістр ecx (лічильник), заносимо розмір масиву, в даному випадку ArrSize = 9, дії які містять після мітки cout призначенні для виводу чисел масиву. Алгоритм cout: робимо декремент регістру ecx, аби перейти на перше значення масиву, заносимо елемент масиву в регістр eax виконавши mov eax,[Arr+ecx*4] і виводимо дане значення, після виводу перевіряємо чи значення ecx>0 якщо так то преходим на мітку cout виконавши jg cout, якщо ж значення ecx менше, то продовжуємо виконання коду. Алгоритм виконання Looper: перед виконанням алгоритму який міститься за міткою Looper ми оновлюємо значення лічильника mov ecx,ArrSize. Перевіряємо значення регістру ecx, якщо воно рівне нулю то виконуємо перехід на мітку EXIT, якщо більше за нуль то робимо декремент (dec ecx) і заносимо значення масиву в регістр eax, після чого порівнюємо його з нулем, якщо значення більше за 0 то переходимо на мітку CountPP де відбувається інкремент значення Count і виконання переходу на мітку Looper, якщо ж значення елементу менше за нуль то здійснюється перехід на мітку Looper для подальшого пошуку додатнього значення масиву. Після пошуку додатних елементів масиву відбудеться перехід на мтіку EXIT, де буде виведене значення Count.
Висновок: Після виконання даної лабораторної роботи я ознайомилась з програмною моделлю 32-розрядних мікропроцесорів Intel та оволоділа навиками створення програм, використовуючи 32-розрядний Асемблер.