гос / sp-lect (1)
.pdfзначення або регістра ESI|RSI, або регістра EDI|RDI, або пари регістрів ESI|RSI та EDI|RDI одночасно.
Напрямок обробки залежить від значення прапорця DF регістру прапорців RFLAGS:
0, автоінкрементна адресація (обробка у бік старших адрес)
DF =
1, автодекрементна адресація (обробка у бік молодших адрес)
Команда CLD встановлює DF=0, що визначає напрямок обробки елементів ланцюжка з молодших адрес пам’яті в старші за рахунок автоматичного збільшення командами обробки ланцюжків значень індексних регістрів ESI|RSI та|або EDI|RDI на число, що дорівнює розміру в байтах елементів ланцюжків.
Команда STD встановлює DF=1, що визначає напрям обробки елементів ланцюжка із старших адрес пам’яті в молодші за рахунок автоматичного зменшення значень індексних регістрів ESI|RSI та|або EDI|RDI на , що дорівнює розміру в байтах елементів ланцюжків.
При старті додатку в середовищі 32-розрядної ОС Windows NT сегментні регістри DS та ES міститимуть однакові значення. Таким чином, завантаження селектору в сегментний регістр виконувати не потрібно, адреса ланцюжка- джерела визначається по вмісту регістру ESI, адреса ланцюжка-приймача –
EDI.
В 64-розрядній ОС Windows NT сегментні регістри взагалі не використовується і завантаження селектору в сегментний регістр виконувати не потрібно, адреса ланцюжка-джерела визначається по вмісту регістру RSI, адреса ланцюжка-приймача – RDI.
3.5.1.6.2 Команди завантаження елементів з ланцюжка
LODSB |
байт |
|
|
|
|
Команда LODSW |
копіює слово |
із комірки пам’яті за адресою в |
LODSD |
подвійне слово |
|
|
|
|
LODSQ |
зчетверене слово |
|
AL
AX збільшує, якщо DF=0
регістрах (DS:ESI)|RSI в регістр і значення
EAX зменшує, якщо DF=1
RAX
1
2
регістра ESI|RSI на не змінюючи значення регістра прапорців RFLAGS
4
8
3.5.1.6.3 Команди збереження елемента в ланцюжку
STOSB |
байт |
AL |
|
|
|
|
|
Команда STOSW копіює слово |
з регістру AX |
в комірку |
|
STOSD |
подвійне слово |
EAX |
|
|
|
|
|
STOSQ |
зчетверене слово |
RAX |
|
пам’яті за адресою |
(ES:EDI)|RDI і збільшує, якщо DF=0 значення регістра |
||
|
зменшує, якщо DF=1 |
|
|
1
2
EDI|RDI на не змінюючи значення регістра прапорців RFLAGS
4
8
3.5.1.6.4 Команди сканування ланцюжка
SCASB |
AL |
байта |
|
|
|
|
|
Команда SCASW |
вираховує з регістру AX |
значення слова |
в |
SCASD |
EAX |
подвійного слова |
|
|
|
|
|
SCASQ |
RAX |
зчетвереного слова |
|
пам’яті за адресою в регістрах (ES:EDI)|RDI, |
збільшує, якщо DF=0 |
значення |
|
|
|
зменшує, якщо DF=1 |
|
1
2
регістра EDI|RDI на і встановлює прапорці OF, SF, ZF, AF, PF, CF
4
8
відповідно до результату операції, не зберігаючи сам результат.
3.5.1.6.5 Команди порівняння ланцюжків
CMPSB |
байта |
|
|
|
|
|
|
Команда CMPSW |
вираховує значення слова |
в комірці пам’яті за |
|
CMPSD |
подвійного слова |
|
|
|
|
|
|
CMPSQ |
зчетвереного слова |
|
|
|
|
байта |
|
|
|
|
|
адресою в регістрах (DS:ESI)|RSI і значення |
слова |
в комірці |
|
|
|
подвійного слова |
|
|
|
|
|
|
|
зчетвереного слова |
|
пам’яті за адресою в регістрах (ES:EDI)|RDI, |
збільшує, якщо DF=0 |
значення |
|
|
|
зменшує, якщо DF=1 |
|
1
2
регістрів ESI|RSI та EDI|RDI на і встановлює прапорці відповідно до
4
8
результату операції, не зберігаючи сам результат.
3.5.1.6.6 Команди пересилання ланцюжків
MOVSB |
байта |
|
|
|
|
Команда MOVSW |
копіює значення слова |
із комірки пам’яті за |
MOVSD |
подвійного слова |
|
|
|
|
MOVSQ |
зчетвереного слова |
|
адресою в регістрах (DS:ESI)|RSI в комірку пам’яті за адресою в (ES:EDI)|RDI і
1
збільшує, якщо DF=0 2
значення регістрів ESI|RSI та EDI|RDI на
зменшує, якщо DF=1 4
8
3.5.1.6.7 Префікси повторення
Автоматична зміна вмісту регістрів ESI|RSI та|або EDI|RDI абсолютно всіма командами обробки ланцюжків робить їх зручними для використання в циклах, для чого, власне, це й передбачалося. Наприклад, пошук в ланцюжку певного значення без використання і з використання команд обробки ланцюжків може виконуватися наступним чином:
mov ecx,<довжина ланцюжка> |
mov ecx,<довжина ланцюжка> |
lea edi,<приймач> |
lea edi,<приймач> |
mov al,<значення для пошуку> |
mov al,<значення для пошуку> |
@@: cmp al,byte ptr [edi] |
@@: scasb |
jz @F |
loopne @B |
inc edi |
@@: …………………………. |
loop @B |
|
@@: …………………………. |
|
Як видно з прикладу, використання автоінкрементної/автодекрементної адресації, властивої командам обробки ланцюжків, зменшує кількість використаних команд. Ще більше скорочення коду дає використання команд обробки ланцюжків, обидва операнда яких можуть розташовуватися у пам’яті. Наприклад, пересилання з одного ланцюжка байтів в інший без використання і з використанням команд обробки ланцюжків може виконуватися наступним чином:
mov ecx,<довжина ланцюжка> |
mov ecx,<довжина ланцюжка> |
|
lea esi,<джерело> |
|
lea esi,<джерело> |
lea edi,<приймач> |
|
lea edi,<приймач> |
@@: mov al, byte ptr [esi] |
@@: |
movsb |
mov byte ptr [edi],al |
|
loop @B |
inc esi
inc edi
loop @B
Як видно з прикладів, при використанні команд обробки ланцюжків, тіло циклу складається з єдиної команди. Для вирішення типових задач пов’язаних з обробкою ланцюжків замість команд передавання управління краще використовувати так звані префікси повторення.
Префікс REP примушує командну обробки ланцюжків повторюватися поки RСХ|ЕСХ не рівний нулю (RСХ|ЕСХ раз).
Префікс REPE|REPZ примушує командну обробки ланцюгів повторюватися поки RСХ|ЕСХ не рівний нулю і прапорець нуля встановлений
(ZF=1).
Префікс REPNE|REPNZ примушує командну обробки ланцюгів повторюватися поки RСХ|ЕСХ не рівний нулю і прапорець нуля скинуто
(ZF=0).
З використанням префіксів повторення попередні приклади будуть
виглядати наступним чином: |
|
; Пересилання ланцюжка |
; Пошук в ланцюжку |
mov ecx,<довжина ланцюжка> |
mov ecx,<довжина ланцюжка> |
lea esi,<джерело> |
lea edi,<приймач> |
lea edi,<приймач> |
mov al,<значення для пошуку> |
rep movsb |
repne scasb |
4 ОБРОБКА СТРУКТУРОВАНИХ ТИПІВ ДАНИХ
4.1 Одновимірні масиви
Масив це структурований тип даних, що являє собою множину із певної кількості елементів однакового типу. Одновимірні масиви можна назвати природною для процесору формою подання даних, оскільки пам’ять має лінійну адресну структуру і її можна розглядати як одновимірний масив.
Для роботи з масивами зокрема, а в загальному випадку з будь якими структурованими даними, зручно використовувати оператори MASM, які описані у наступній таблиці.
Таблиця 6.1 – Оператори MASM для роботи з типами даних
Оператор Опис
Повертає число байтів у змінній, що було виділено при її початковій ініціалізації.
Повертає число байтів, що займає змінна або тип.
Повертає число елементів даних у змінній, що було створено при її початковій ініціалізації.
Повертає число об’єктів даних у змінній.
Повертає тип (розмір у байтах) виразу.
Обробка одновимірних масивів, як правило, передбачає використання принаймні одного циклу, а в загальному випадку і вкладених циклів. Для звернення до елементів масиву можна використовувати різні способи адресації операндів у пам’яті. Вибір конкретного способу адресації залежить від задачі, що вирішується.
Приклад 4.1 Визначення суми позитивних елементів у масиві байт, і кількість негативних елементів у масиві слів.
.386
.model flat, stdcall option casemap :none
.data
xdb 1,2,3,-4,-5,6,0,7,8,0,5,8
y |
dw 1,2,3,-4,-5,6,0,7,8,0,5,0 |
.code |
|
start: lea esi,x xor ebx,ebx
mov ecx, lengthof x m1: cmp byte ptr [esi],0
jle m2
add bl, byte ptr [esi] adc bh,0
m2: inc esi loop m1 xor edi,edi lea esi,y
mov ecx,lengthof y m3: cmp word ptr [esi],0
jge m4 inc edi
m4: add esi, type y loop m3
ret end start
Приклад 4.2 Визначення суми позитивних елементів з парними індексами одновимірного масиву слів.
В загальному випадку, при довільному місце розташуванні масиву в пам’яті адреси його елементів можуть бути як парними, так і не парними числами, що будуть зростати для масиву слів з кроком 2. Отже парність чи не парність індексу елементу масиву не можна визначати перевіривши парність чи не парність його адреси.
Для вирішення цієї задачі можна скористатися базовою адресацією, завантаживши спочатку в базовий регістр зміщення другого елементу, а потім в циклі збільшувати значення регістру на 4, просто перестрибуючи елементи з непарними індексами і виконуючи порівняння інших елементів з 0.
При такому методі вирішення задачі виникає необхідність контролю виходу адреси за межі масиву, який можливий при не правильному виборі кількості повторень циклу. Значення лічильнику циклу залежить від парності чи не парності кількості елементів в масиві: в масиві з трьох чисел лише один має парний індекс, і стрибок через третій елемент призводить до виходу за межі
масиву, а в масиві з чотирьох елементів вже два мають парні індекси і такий стрибок треба виконувати.
В наведеному нижче прикладі обрано інший метод адресації елементів масиву – базову індексну адресацію з масштабуванням. По перше, в такому випадку кількість повторень циклу фіксована і визначається тільки кількістю елементів масиву незалежно від того парна вона чи не парна, а по-друге, така адресація дозволяє оперувати не адресами, а логічними індексами 0,1,2,...
елементів масиву, що відповідає нашим інтуїтивним уявленням і використовується мовами програмування високого рівня. Недоліком такого підходу у порівнянні із методом «перестрибування» не потрібних елементів є збільшення кількості виконуваних команд.
Суть базової індексної адресації можна сформулювати словосполученням «зміщення від зміщення». Завантаження в базовий регістр адреси початку масиву схоже на перенесення центру координат в нову вихідну точку. Після цього незалежно від того де розміщується масив в сегменті даних, його елементи адресуються зі зміщення 0 відносно нової точки відліку. Для завдання зміщення «в нових координатах» використовується індексний регістр, який для узгодженості з розміром даних множиться на розмір елементу масиву в байтах (2 для слова). При цьому значення індексного регістру (недарма ж він зветься індексним) збільшується в циклі в природному для індексів елементів масиву порядку 0,1,2,3,..., що і дозволяє звертатися до елементів масиву за їх номерами, незалежно від того, які адреси пам’яті насправді їм відповідають.
.386
.model flat, stdcall
option casemap :none
.data
x dw 1,2,3,-4,-5,6,0,7,8,0,5,8,3
.code
start:
lea ebx,x
xor eax,eax
xor edx,edx xor esi,esi
mov ecx,lengthof x next: test esi,1
jnz @F
cmp word ptr [ebx+esi*2],0 jle @F
add ax, word ptr [ebx+esi*2] adc dx,0
@@: inc esi loop next push dx push ax pop ebx ret
end start
Приклад 4.3 Визначення максимального елементу у масиві слів (значення та індексу).
.386
.model flat, stdcall option casemap :none
.data
x dw 7,8,0,5,8,3,-10,5,0,0,14,-5,3,-7
.code
start: lea ebx,x xor esi,esi mov ax,[ebx] mov edi,esi
mov ecx, lengthof x dec ecx
inc esi
next: cmp [ebx+esi*2],ax jle @F
mov ax,[ebx+esi*2]
mov edi,esi
@@: inc esi
loop next
ret end start
Цей приклад дозволить нам також продемонструвати використання групи команд CMOVcc, яку ми пропустили при розгляді команд пересилання.
Команди передачі керування погано впливають на роботу конвейера і кеш пам’яті процесора, які використовуються для підвищення його продуктивності. Це одна з причин, з якої було введено префікси повторення для використання з командами обробки ланцюжків замість не зручних для процесору команд передачі керування.
В подальшому для зменшення розгалуджень в програмах при вирішенні деяких типових задач програмування в систему команд було введено групу команд умовного пересилання. Узагальнене мнемоничне позначення команд цієї групи CMOVcc r, r/m. Вони дозволяють виконати завантаження у регістр значення з пам’яті або іншого регістру при виконанні умови, узагальнене мнемонічне позначення якої «сс» співпадає з використовуваним при описі команд умовної передачі керування. Самі команди не впливають на стан RFLAGS і тому припустимо використання декількох команд CMOVcc одна за одною.
|
Приклад 4.4 Визначення значення та індексу максимального елементу у |
масиві слів з використанням команд умовного пересилання. |
|
.686 |
; для CMOVcc |
.model flat, stdcall
option casemap:none
.data
x dw 7,8,0,5,8,3,-10,5,0,0,14,-5,3,-7
.code
start: lea ebx,x
