Средства ассемблера для обработки символьных данных

Команды обработки строк символов или цепочечные команды.

Различие: Под строкой символов понимается последовательность байтов, а цепочка - это более общее название для случаев, когда размер элемента последовательности превышает байт и составляет слово или двойное слово. Т.о. цепочечные команды позволяют проводить действия над блоками памяти, представляющими собой последовательности элементов следующего размера:

8 битов, т. е. байт;

16 битов, т. е. слово;

32 бита, т. е. двойное слово.

Содержимое этих блоков для процессора не имеет значения. Это могут быть символы, числа и любая информация. Главное, чтобы размеры элементов соответствовали одному из перечисленных вариантов и эти элементы находились в соседних ячейках памяти.

Всего в системе команд поддерживается семь операций обработки цепочек.

1. Пересылка цепочки:

MOVS адрес_приемника, адрес_источника

MOVSB (переслать цепочку байтов)

MOVSW (переслать цепочку слов)

MOVSD (переслать цепочку двойных слов)

2. Сравнение цепочек

CMPS адрес_приемника, адрес_источника

CMPSB

CMPSW

CMPSD

3. Сканирование цепочки

SCAS адрес_приемника

SCASB

SCASW

SCASD

4. Загрузка элемента из цепочки

LODS адрес_источника

LODSB

LODSW

LODSD

5. Сохранение элемента в цепочке

STOS адрес_приемника

STOSB

STOSW

STOSD

 

К этим командам относятся префиксы повторения.

REP

REPE или REPZ

REPNE или REPNZ

Эти префиксы повторения указываются перед нужной цепочечной командой в поле метки. Цепочечная команда без префикса повторения выполняется один раз. Размещение префикса перед цепочечной командой заставляет ее выполняться в цикле.

ПП REP используется с командами, реализующими операции пересылки и сохранения элементов цепочек. ПП REP

заставляет данные команды выполняться, пока содержимое CX не станет равным 0. При этом команда, перед которой стоит префикс, автоматически уменьшает содержимое CX на 1.

ПП REPNE или REPNZ заставляют цепочечную команду циклически выполняться до тех пор, пока содержимое CX не равно 0 или флаг ZF равен 0. При нарушении одного из этих условий работа команды прекращается. Данные префиксы можно тоже использовать с командами сравнения и сканирования цепочки, но для поиска совпадающих элементов цепочек.

Цепочка-источник, адресуемая операндом адрес_источника, может находиться в текущем сегменте данных, определяемо`м регистром DS. Цепочка-приемник, адресуемая операндом адрес_приемника, должна быть в дополнительном сегменте данных, адресуемом сегментным регистром ES.

Есть две возможности направления обработки цепочки:

- от начала цепочки к ее концу, т. е. в направлении возрастания адресов;

- от конца цепочки к началу, т. е. в направлении убывания адресов.

Если DF=0, обработка осуществляется в направлении возрастания адресов. Если DF=1, то обработка будет идти в направлении убывания адресов.

Состоянием флага DF можно управлять с помощью двух команд, не имеющих операндов:

CLD - очистить флаг направления (команда сбрасывает флаг направления DF в 0);

STD - установить флаг направления (команда устанавливает флаг направления DF в 1).

1) Команда копирует байт, слово или двойное слово из цепочки, адресуемой операндом адрес_источника, в цепочку, адресуемую операндом адрес_приемника. Сама по себе команда MOVS пересылает только один элемент. Опишем последовательность действий, которые нужно выполнить в программе для того, чтобы переслать цепочку элементов из одной области памяти в другую.

- Установить значение флага DF в зависимости от того, в каком направлении будут обрабатываться элементы цепочки.

- Загрузить указатели на адреса цепочек в памяти в пары регистров DS:SI и ES:DI.

- Загрузить в регистр CX количество обрабатываемых элементов.

- Выдать команду MOVS с префиксом REP.

 

Пример пересылки строк

.data

source db «Тестируемая строка»,’$’

dest db 19 dup (“ ”)

.code

assume ds:data, es:data

main:

mov ax, data

mov ds, ax

mov es, ax

cld

lea si, source

lea di, dest

mov cx, 20

rep movs dest, source

lea dx, dest

mov ah, 09h

int 21h

exit:

mov ax, 4c00h

int 21h

end main

 

Аналогичный пример с командой MOVSB:

.data

sourse db «Пересылаемая строка$»

dest db 20 dup (?)

.code

assume ds:data, es:data

main:

mov ax, data

mov ds, ax

mov es, ax

cld

lea si, source

lea di, dest

mov cx,20

rep movsb

…..

2) Команда производит сравнение элементов цепочки-источника с элементами цепочки-приемника.

- адрес_источника определяет адрес цепочки-источника в сегменте данных, заранее загружаемый в пару регистров DS:SI;

- адрес_приемника определяет адрес цепочки-приемника, которая должна находиться в дополнительном сегменте, заранее загружаемый в пару регистров ES:DI.

Алгоритм работы команды CMPS заключается в последовательном выполнении вычитания над очередными элементами обеих цепочек. Принцип выполнения вычитания такой же как у команды сравнения CMP. Чтобы заставит команду CMPS выполняться несколько раз, т. е. произвести последовательное сравнение элементов цепочек, необходимо перед командой определить ПП. С данной командой можно использовать все ПП.

3) Команда сканирования цепочек, производит поиск некоторого значения в области памяти.

Команда имеет один операнд, обозначающий местонахождение цепочки в дополнительном сегменте. Принцип поиска тот же, что и в команде сравнения СМРS, то есть последовательное выполнение вычитания (содержимое регистра аккумулятора минус содержимое очередного элемента цепочки). В зависимости от результатов вычитания производится установка флагов, при этом сами операнды не изменяются. Команда SCAS с префиксом REPE/REPZ позволяет найти элемент цепочки, отличающийся по значению от заданного в аккумуляторе. Команда SCAS с префиксом REPNE/REPNZ позволяет найти элемент цепочки, совпадающий по значению с элементом в аккумуляторе.

4) Операция загрузки элемента цепочки в аккумулятор позволяет извлечь элемент цепочки и поместить его в регистр-аккумулятор AL,AX. Эту операцию удобно использовать вместе с поиском (сканированием) с тем, чтобы, найдя нужный элемент, извлечь его. Возможный размер извлекаемого элемента определяется применяемой командой.

Команда имеет один операнд, обозначающий строку в основном сегменте данных. Работа команды заключается в том, чтобы извлечь элемент из цепочки по адресу, соответствующему содержимому пары регистров DS:ESI и поместить его в регистр AX/AX/AL. Эту команду удобно использовать после команды SCAS, локализующей местоположение искомого элемента в цепочке. Префикс повторения в этой команде может и не понадобиться.

5) Операция переноса элемента из аккумулятора в цепочку позволяет произвести действие, обратное действию команды LODS, то есть сохранить значение из регистра-аккумулятора в элементе цепочки. Эту операцию удобно использовать вместе с операциями поиска (сканирования) SCANS и загрузки LODS с тем, чтобы, найдя нужный элемент, извлечь его в регистр и записать на его место новое значение.

Команда имеет один операнд адрес_приемника, адресующий цепочку в дополнительном сегменте данных. Команда пересылает элемент из аккумулятора (регистра AX/AX/AL) в элемент цепочки по адресу, соответствующему содержимому пары регистров ES:DI. Префикс повторения в этой команде может и не понадобиться.

 

Особенности строковых команд:

1) они не используют обычные методы адресации.

2) строка состоит из многих элементов, но команды обработки работают только с одним в каждый момент времени.

3) в зависимости от состояния флага DF строковые команды увеличиваются или уменьшаются на 1 байт или слово содержимое регистров SI,DI.

4) в командах, указывающих размер обрабатываемых слов, операнды не обязательны (в MOVSB и т.д.).

5) при использовании префикса REP при выходе из цикла содержимое SI и DI инкрементируется автоматически, и они будут указывать на следующие байты за структурами.

 

 

Операции над списками

Анализ текущего элемента списка. Пусть надо определить, равен ли элемент текущего звена списка значению некоторой переменной X и, если да, перейти на метку EQ.

 

mov AX, ES:[BX].ELEM

cmp AX,X

je EQ

 

Переход к следующему звену списка. Пусть регистр BX указывает на некоторое звено списка и требуется перейти к следующему звену списка. В регистр BX надо записать адрес следующего звена. Адрес этот находится в поле NEXT текущего звена, поэтому его надо перенести в BX.

 

mov BX, ES:[BX].NEXT

 

Проверка на конец списка.

 

cmp BX, NIL

je LIST_END

 

На этих простейших операциях над списками строятся более крупные операции - просмотр списка, удаление элемента из списка, вставка нового элемента в список.

 

МАКРОСРЕДСТВА

Макроязык

 

Часто бывает необходимым предварительное преобразование текста программы. Может потребоваться, чтобы какой-то фрагмент программы был продублирован несколько раз или чтобы в зависимости от некоторых условий в тексте программы были сохранены одни фрагменты и удалены другие. Такую возможность предоставляют макросредства. Расширения языка ассемблера за счет этих средств обычно называют макроязыком.

Программа написанная на макроязыке, транслируется в 2 этапа. Сначала она преобразуется к виду, где нет никаких макросредств. Этот этап называется этапом макрогенерации, его осуществляет специальный транслятор - макрогенератор. На втором этапе полученная программа переводится на машинный язык. Это этап ассемблирования.

Трансляция программы, написанной на макроязыке, занимает больше времени, чем трансляция программы на «чистом» языке ассемблера. Но макроязык предоставляет больше возможностей, чем просто язык ассемблера, и поэтому писать программы, особенно большие, на макроязыке проще.

 

Блоки повторения

 

Иногда в некотором месте программы приходится выписывать несколько раз подряд один и тот же фрагмент. Как правило, требуется чтобы, фрагмент описанный один раз, макрогенератор размножал нужное число раз. Такая возможность реализуется с помощью блоков повторения. Блок повторения имеет следующую структуру:

 

<заголовок>

<тело>

ENDM

 

<тело> - любое число любых предложений.

Встречая в исходном тексте программы такой блок, макрогенератор подставляет вместо него в окончательную программу несколько копий тела. При дублировании тело может выписываться без каких-либо изменений, а может копироваться и с модификациями. Как именно происходит дублирование, зависит от заголовка блока. Имеется 3 разновидности заголовка.

 

REPT-блоки

 

REPT k

<тело>

ENDM

 

k - константное выражение с неотрицательным значением. Макрогенератор создает k точных копий тела блока и подставляет их в окончательный текст программы.

 

REPT 3 SHR AX, 1

SHR AX, 1 SHR AX, 1

ENDM SHR AX, 1

 

N EQU 6 N EQU 6

REPT N-4 DB 0, 1

DB 0, 1 DW?

DW? DB 0, 1

ENDM DW?

 

IRP-блоки

 

IRP p, <v1,…, vk>

<тело>

ENDM

 

p - некоторое имя, играет роль формального параметра и может использоваться в предложениях тела. vi - фактические параметры. Встречая такой блок, макрогенератор заменяет его на k копий тела, причем в i-й копии все вхождения имени p заменяются на vi.

 

IRP REG, <AX,CX,SI> PUSH AX

PUSH REG PUSH CX

ENDM PUSH SI

 

Формальный параметр локализуется в теле блока и может быть любым именем.

 

IRP BX, <1, 5> ADD AX, 1

ADD AX, BX ADD AX, 5

ENDM

 

В теле блока повторения заменяется только формальный параметр, другие имена переносятся в копии тела без изменений.

 

N EQU 1 N EQU 1

IRP P, <A,B> A EQU N

P EQU N B EQU N

ENDM

 

 

IRPC-блоки

 

IRPC p, s1….sk

<тело>

ENDM

 

p - формальный параметр, si - символ. Встречая такой блок, макрогенератор заменяет его на k копий тела блока, причем в i-й копии все вхождения параметра p будут заменены на символ si.

 

IRPC D, 17W ADD AX, 1

ADD AX, D ADD AX, 7

ENDM ADD AX, W

 

МАКРОСЫ

 

С помощью блока повторения один раз описывается фрагмент программы, который затем многократно копируется. Но блоки повторения можно использовать, только если эти копии должны быть расположены рядом друг с другом. Если фрагмент должен повторяться, но в разных местах программы, используют макросы. Специальным образом описывается этот фрагмент программы и ему дается имя, а затем в нужных местах программы указывается ссылка на этот макрос.

Описание макроса - макроопределение.

Ссылка на макрос - макрокоманда.

Процесс замены макрокоманды на макрос - макроподстановка, а результат подстановки - макрорасширение.

Использование макрокоманд позволяет:

- упростить и сократить исходный текст программы;

- сделать программу более понятной;

- уменьшить число возможных ошибок кодирования.

 

Макроопределения

 

описание макроса имеет следующий вид:

 

<имя макроса> MACRO <формальные параметры через запятую>

<тело макроса>

ENDM

 

SUM MACRO X, Y; X=X+Y

MOV AX, Y

ADD X, AX

ENDM

 

Существует 3 варианта размещения макроопределения:

1) Макроопределения могут располагаться в начале исходного текста программы до сегмента кода и данных с тем, чтобы не ухудшать читаемость программы. Этот вариант следует применять в случаях, если определяемые макрокоманды актуальны только в пределах одной этой программы.

2) Макроопределения могут располагаться в отдельном файле. Этот вариант подходит при работе над несколькими программами одной проблемной области. Чтобы сделать доступными эти макроопределения в конкретной программе, необходимо в начале исходного текста этой программы записать директиву:

include имя_файла

 

Там где указана эта директива, будет вставлен текст указанного файла.

3) Макроопределения могут располагаться в макробиблиотеке. Если есть универсальные макрокоманды, которые используются практически во всех программах, то их целесообразно записывать в макробиблиотеку.

 

Функционально макроопределения похожи на процедуры. Сходство в том, что и те, и другие достаточно один раз описать, а затем вызывать их специальным образом. Отличия:

- В отличие от процедуры, текст которой неизменен, макроопределение в процессе макрогенерации может меняться в соответствии с набором фактических параметров. При этом коррекции могут подвергаться как операнды команд, так и сами команды.

- При каждом вызове макрокоманды ее текст в виде макрорасширения вставляется в программу. При вызове процедуры процессор осуществляет передачу управления на начало процедуры, находящейся в некоторой области памяти в одном экземпляре. Код в этом случае получается более компактным, хотя быстродействие несколько снижается за счет необходимости осуществления переходов.

Макроопределение обрабатывается компилятором особым образом. Для того чтобы использовать описанное макроопределение, его нужно активизировать с помощью макрокоманды. Для этого в нужном месте исходного кода программы на основе текста заголовка макроопределения указывается следующая конструкция:

 

имя_макрокоманды список_фактических_аргументов

 

Директивы условной компиляции

 

Существует 2 вида директив:

- Директивы компиляции по условию позволяют проанализировать определенные условия в ходе генерации макрорасширения и при необходимости изменить этот процесс;

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

С этими директивами применяются упомянутые ранее директивы управления процессом генерации макрорасширений EXITM и GOTO.

Директива EXITM не имеет операндов, она немедленно прекращает процесс генерации макрорасширения, как только встречается в макроопределении. Это дает возможность сократить объем исходного кода путем удаления неиспользуемых команд.

Директива GOTO имя_метки переводит процесс генерации макроопределения в другое место, прекращая тем самым последовательное разворачивание строк макроопределения.

 


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: