Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
л.р.1-12.СПРГ-1(40, укр).doc
Скачиваний:
5
Добавлен:
27.08.2019
Размер:
2.33 Mб
Скачать

Лабораторна робота 8.

Тема: Ланцюжкові команди Асемблера

Ціль: Вивчити основні прийоми обробки рядків у програмах на асемблері.

1. Підготовка до роботи.

Ознайомитися з набором ланцюжкових команд [2, стор. 120-160], [3, стор. 393-403].

2. Питання для самоконтролю.

  1. Особливості команд порівняння рядків.

  2. Команди копіювання рядків.

  3. Команди порівняння рядків.

  4. Команди завантаження та вивантаження рядків.

  5. Команди сканування рядків.

  6. Як визначити розмір рядків?

  7. Регістри, що використовуються при обробці рядків.

3. Короткі теоретичні відомості.

Більшість команд асемблера оперують байтом, словом або подвійним словом. Однак у багатьох випадках буває необхідно переслати або порівняти поля даних, що перевищують по розмірі слово або байт. Наприклад, може знадобитися порівнювати описи або імена, щоб відсортувати них у визначеній послідовності. Елементи такого формату відомі як строкові дані і можуть мати як символьний (ASCII – коди, UNICODE), так і числовий тип (відповідний символьному кодові).

Переваги асемблера виявляються і при обробці рядків і масивів даних. Під операціями обробки рядків ми будемо розуміти наступні операції:

  • порівняння двох рядків;

  • копіювання рядка-джерела в рядок-приймач;

  • перебування підрядку в заданому рядку;

  • об'єднання двох рядків (конкатенація);

  • зчитування рядків із пристрою або файлу;

  • запис рядка в пристрій або файл;

  • визначення розміру рядка;

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

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

Можна вказати розмір рядка (кількість елементів, що входять у рядок), записавши число елементів у перший байт рядка. По загальноприйнятих угодах перший елемент рядка має зсув 0, тому можна сказати, що розмір рядка прописується в нульовому елементі, а символи рядка починаються з першого елемента. Такий принцип був реалізований у мові Pascal і в середовищі програмування Delphi. Такі рядки називаються короткими (short strings), оскільки їхній розмір не перевищує 255 байт.

Найбільше поширення одержав другий спосіб ідентифікації рядка, при якому наприкінці рядка вказується нульовий символ (0). Такі рядки називаються рядками з завершальним нулем (null-terminated strings). Вони використовуються в мові Сі в операційних системах Windows. От як виглядає такий рядок мовою асемблера:

String DB “NULL-TERMINATED STRING”,0

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

str1 DB “STRING”

len $-s1 (fasm) або

len equ $-s1 (masm)

Тут визначений рядок символів s1, а її розмір len дорівнює різниці початкової і кінцевої адрес елементів. Такий варіант дуже зручний, оскільки константу len можна використовувати для циклічної обробки елементів рядка. При цьому 1en міститься в лічильник символів (звичайно регістр СХ або ЕСХ, хоча можуть бути й інші регістри).

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

str1 DB “TEST STRING”,0

lea esi, [str1] ; адреса першого елемента рядка

cmp byte ptr [ESI],0 ; перевірка на кінець рядка

Якщо використовувати формат коротких рядків (стиль Pascal), то обробку елементів можна організувати так, як показано в наступному прикладі:

str1 db 7,“string1”

lea esi, str1

mov cl, byte ptr [ESI]

inc esi

У цьому випадку обробка рядка починається з елемента з індексом 1, тобто находящегося по адресі [esi+1]. У регістр CL міститься розмір рядка str1, рівний 7 (команда mov cl, byte ptr [esi]). Визначеним недоліком такого методу є необхідність заздалегідь знати розмір рядка.

Дотепер ми розглядали рядки, що складаються із символів, але наші міркування застосовні і до послідовності довільних байтів, слів і подвійних слів. У цьому випадку таку сукупність елементів називають масивом. Для байтових масивів дійсні ті ж прийоми роботи, що і для символьних рядків, а от при роботі з елементами розміром у слово або подвійне слово варто враховувати деякі особливості, зв'язані з розмірністю елементів. Крім корекції лічильника необхідно правильно вказувати адреса наступного елемента масиву. Для подвійного слова наступний елемент відстоїть від попередніх на 4 байти, для слова — на 2 байти.

Для роботи з рядками і масивами в систему команд процесорів Intel включені спеціальні команди обробки рядків. Цю групу команд у термінології Intel називають командами строкових примітивів, або ланцюжковими командами. Розглянемо принципи роботи ланцюжкових команд.

Ланцюжкова команда може бути використана для багаторазової обробки одного байта, одного слова або подвійного слова. Для цього вказується префікс повторення rер. Далі приведені модифікації префікса rep для команд строкових примітивів:

  • rер – повторювати операцію, поки СХ не стане рівним 0;

  • repz, rере – повторювати операцію, поки елементи рівні, тобто до першої нерівності (прапор ZF встановлений у 0). Операція припиняється, якщо прапор ZF встановлюється в 1 або лічильник у регістрі есх (сх) досягає нуля;

• repne, repnz – повторювати операцію, поки елементи не рівні, тобто до першої рівності (прапор ZF встановлений у 1). Операція припиняється при установці прапора ZF у 0 або при досягненні значення 0 у регістрі есх (сх).

Для процесорів Intel, що обробляють слово за одну операцію, використання ланцюжкових команд там, де це можливо, підвищує ефективність програми.

У строкових командах не застосовуються способи адресації, характерні для інших команд обробки рядків. Строкові команди адресують операнди комбінаціями регістрів ESI (SI) або EDI (DI).

Операнди джерела використовують регістр ESI (SI), а операнди приймача (результату) — регістр EDI (DI). Усі строкові команди коректують адреса після виконання операції. Рядок може складатися з декількох елементів, але команди обробки рядків можуть обробляти тільки один елемент у кожен момент часу. Автоматичний інкремент (збільшення) або декремент (зменшення) адреси операнда дозволяє швидко обробляти строкові дані. Прапор напрямку DF у регістрі стану визначає напрямок обробки рядків. Якщо він дорівнює 1, то адреса зменшується, а якщо він скинутий у 0, то адреса збільшується. Сама величина інкременту або декременту адреси визначається розміром операнда. Наприклад, для символьних рядків, у яких розмір операндів дорівнює 1 байт, команди обробки рядків змінюють адресу на 1 послу кожної операції. Якщо обробляється масив цілих чисел, у якому кожен операнд займає 4 байти, то строкові команди змінюють адреса на 4. Після виконання операції покажчик адреси в регістрах ESI (SI) або EDI (DI) посилається на наступний елемент рядка.

Ми буде розглядати в основному рядки з завершальним нулем.

Кожна команда обробки рядків має три припустимих формати. Суфікси b, w і d визначають крок інкременту і декременту для індексних регістрів ESI (SI) і EDI (DI). Якщо команда використовується в загальному форматі, то розмірність операндів повинна бути визначена явно.

Перед виконанням команд строкових примітивів необхідно завантажити в регістри ESI (SI) і/або EDI (DI) адреси оброблюваних комірок пам'яті.

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

Найбільш продуктивний код можна написати, якщо дотримувати кілька умов:

  • дані в джерелі і приймачі повинні бути вирівняні по 8-байтовой границі;

  • прапор напрямку повинний бути встановлений убік збільшення адрес;

  • лічильник у регістрі ЕСХ повинний мати значення, більше або, принаймні, рівне 64;

• різниця між умістом регістрів ESI і EDI повинна бути більше або дорівнює 32. При розробці високоефективного коду бажано все-таки уникати ланцюжкових команд, використовуючи замість них комбінацію mov/inc або mov/dec.

Таблиця 1. Строкові операції (ланцюжкові команди).

Команда

Призначення

Алгоритм роботи

Команди пересилання MOVS

MOVS приймач, джерело

Копіювання рядка

Приймач <== джерело

MOVS (B/W/D)

Копіювання рядка байтів (слів або подвійних слів)

[DI] <= [SI] або [EDI] <= [ESI]

Команди порівняння CMPSx

CMPS приймач, джерело

Порівняння рядків

Приймач ~ джерело

CMPS (B/W/D)

Порівняння рядків байтів (слів або подвійних слів)

[DI] ~ [SI] або [EDI] ~ [ESI]

Команди сканування SCASx

SCAS приймач

Сканування рядка

Приймач ~ АХ (або AL)

SCAS (B/W/D)

Сканування рядка байтів (слова або подвійного слова)

[DI] ~ AL або [DI] ~ АХ або [EDI] ~ EАХ

Команди завантаження LODSx

LODS джерело

Читання з рядка

Джерело => АХ (AL)

LODS (B/W/D)

Читання байта (слова або подвійного слова) з рядка

[SI]=>AL або [SI]=> АХ або [ESI]=>EAX

Команди збереження STOSx

STOS приймач

Запис у рядок

АХ (або AL) ==> приймач

STOS (B/W/D)

Запис байта (слова або подвійного слова) у рядок

AL => [DI] або АХ=> [DI] або [ESI]=>EAX

Префікси повторення REPx (повторення)

REP

Повторювати команду

REPE/REPZ

Повторювати команду, поки дорівнює (прапор ZF= 1)

REPNE/REPNZ

Повторювати команду, поки НЕ дорівнює (прапор ZF=0) |

Команди роботи з прапором DF (напрямок обробки)

CLD

DF=0 – убік старших адрес

STD

DF=1 – убік молодших адрес

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]