Команды обработки строк
------------------------------------------------------------
Команды обработки строк
Цель: Ообъяснить назначение специальных цепочечных команд, используемых для обработки символьных данных.
ВВЕДЕНИЕ ------------------------------------------------------------
Команды, показанные в предыдущих главах, оперировали одним байтом, или одним словом за одно выполнение. Часто, однако, бывает необходимо переслать или сравнить поля данных, которые превышают по длине одно слово. Например, необходимо сравнить описания или имена для того, чтобы отсортировать их в восходящей последовательности. Элементы такого формата известны как строковые данные и могут являтся как символьными, так и числовыми. Для обработки строковых данных ассемблер имеет пять команд обработки строк:
MOVS переслать один байт или одно слово из одной области памяти в другую;
LODS загрузить из памяти один байт в регистр AL или одно слово в регистр AX;
STOS записать содержимое регистра AL или AX в память;
CMPS сравнить содержимое двух областей памяти, размером в один байт или в одно слово;
SCAS сравнить содержимое регистра AL или AX с содержимым памяти.
Префикс REP позволяет этим командам обрабатывать строки любой длины.
СВОЙСТВА ОПЕРАЦИЙ НАД СТРОКАМИ ------------------------------------------------------------
Цепочечная команда может быть закодирована для повторяю щейся обpаботки одного байта или одного слова за одно выполнение. Например, можно выбрать "байтовую" команду для обработки строки с нечетным числом байт или "двухбайтовую" команду для обработки четного числа байт. Ниже перечислены регистры, участвующие в цепочечных командах (для однобайтовых и двухбайтовых вариантов). Предположим, что регистры DI и SI содержат необходимые адреса:
Команда Операнды Байт Слово MOVS DI,SI MOVSB MOVSW LODS AL,SI или AX,SI LODSB LODSW
Ассемблер для IBM PC. Глава 11 242
STOS DI,AL или DI,AX STOSB STOSW CMPS SI,DI CMPSB CMPSW SCAS DI,AL или DI,AX SCASB SCASW
Например, можно кодировать операнды для команды MOVS, но опустить их для MOVSB и MOVSW. Эти команды предполагают, что pегистры DI и SI содержат относительные адреса, указывающие на необходимые области памяти (для загрузки можно использо вать команду LEA). Регистр SI обычно связан с регистром сегмента данных - DS:SI. Регистр DI всегда связан с регистром дополнительного сегмента - ES:DI. Следовательно, команды MOVS, STOS, CMPS и SCAS требуют инициализации регистра ES (обычно адресом в регистре DS).
REP: ПРЕФИКС ПОВТОРЕНИЯ ЦЕПОЧЕЧНОЙ КОМАНДЫ ------------------------------------------------------------
Несмотря на то, что цепочечные команды имеют отношение к одному байту или одному слову, префикс REP обеспечивает повторение команды несколько раз. Префикс кодируется непо средственно перед цепочечной командой, например, REP MOVSB. Для использования префикса REP необходимо установить начальное значение в регистре CX. При выполнении цепочечной команды с префиксом REP происходит уменьшение на 1 значения в регистре CX до нуля. Таким образом, можно обрабатывать строки любой длины. Флаг направления определяет направление повторяющейся операции: - для направления слева направо неоходимо с помощью команды CLD установить флаг DF в 0; - для направления справа налево необходимо с помощью команды STD установить флаг DF в 1. В следующем примере выполняется пересылка 20 байт из STRING1 в STRING2. Предположим, что оба регистра DS и ES инициализированы адресом сегмента данных:
STRING1 DB 20 DUP('*') STRING2 DB 20 DUP(' ')
... CLD ;Сброс флага DF MOV CX,20 ;Счетчик на 20 байт LEA DI,STRING2 ;Адрес области "куда" LEA SI,STRING1 ;Адрес одласти "откуда" REP MOVSB ;Переслать данные
При выполнееии команд CMPS и SCAS возможна установка флагов состояния, так чтобы операция могла прекратиться сразу после обнаружения необходимого условия. Ниже приведены модификации префикса REP для этих целей.
REP - повторять операцию, пока CX не равно 0;
Ассемблер для IBM PC. Глава 11 243
REPZили REPE - повторять операцию,пока флаг ZF показывает "равноили ноль".Прекратить операцию при флаге ZF, указывающему на не равно или не ноль или при CX равном 0; REPNE или REPNZ - повторять операцию, пока флаг ZF показывает "не равно или не ноль". Прекратить операцию при флаге ZF, указывающему на "равно или нуль" или при CX равным 0.
Для процессоров 8086, 80286 и 80386, обрабатывающих слово за oдно выполнение, использование цепочечных команд, где это возможно, приводит к повышению эффективности работы программы.
MOVS: ПЕРЕСЫЛКА СТРОК ------------------------------------------------------------
На рис.7. 5 была показана программа для пересылки девяти байтового поля. Программа включала три команды для инициали зации и пять команд для цикла. Команда MOVS с префиксом REP и длиной в регистре CX может выполнять пересылку любого числа символов более эффективно. Для области, принимающей строку, сегментным регистром, является pегистр ES, а регистр DI содержит относительный адрес области, передающей строку. Сегментным регистром является регистр DS, а регистр SI содержит относительный адрес. Таким образом,в начале программы перед выполнением команды MOVS необходимо инициализировать регистр ES вместе с регистром DS, а также загрузить требуемые относительные адреса полей в регистры DI и SI. В зависимости от состояния флага DF команда MOVS производит увеличение или уменьшение на 1 (для байта) или на 2 (для слова) содержимого регистров DI и SI. Приведем команды, эквивалентные цепочечной команде REP MOVSB:
JCXZ LABEL2 LABEL1: MOV AL,[SI] MOV [DI],AL INC/DEC DI ;Инкремент или декремент UNC/DEC SI ;Инкремент или декремент LOOP LABEL1 LABEL2: ...
В программе на рис. 11.1 процедура C10MVSB использует команду MOVSB для пересылки содержимого десятибайтового поля NAME1 в поле NAME2. Первая команда CLD сбрасывает флаг направления в 0 для обеспечения процесса пересылки слева направо. В нормальном состоянии флаг DF обычно имеет нулевое значение и команда CLD используется из предосторожности.
Ассемблер для IBM PC. Глава 11 244
Две команды LEA загружают регистры SI и DI относительными адресами NAME1 и NAME2 соответственно. Так как регистры DS и ES были ранее инициализированы адресом DATASG, то полные адреса полей NAME1 и NAME2 будут в регистрах ES:DI и DS:SI. (COM программа автоматически инициализирует регистры ES и DS). Команда MOV заносит в регистр CX значение 10 - длину полей NAME1 и NAME2. Команда REP MOVSB выполняет следующее:
ъ Пересылает самый левый байт из поля NAME1 (адресованно го pегистрами ES:DI) в самый левый байт поля NAME2 (адресованного регистрами DS:SI). ъ Увеличивает на 1 адреса в регистрах DI и SI для следующего байта. ъ Уменьшает CX на 1. ъ Повторяет перечисленные действия (в данном случае 10 раз), пока содержимое регистра CX не станет равным нулю.
Поскольку флаг DF имеет нулевое значение, команда MOVSB увеличивает адреса в регистрах DI и SI, и в каждой итерации процесс переходит на байт вправо, т.е. пересылает байт из NAME1+1 в NAME2+1 и т.д. Если бы флаг DF был равен 1, тогда команда MOVSB уменьшала бы адреса в регистрих DI и SI, выполняя процесс справа налево. Но в этом случае регистры SI и DI необходимо инициализировать адресами последних байтов полей, т.е. NAME1+9 и NAME2+9 соответственно. В процедуре D10MVSW (рис.11.1) используется команда MOVSW, пересылающая одно слово за одно выполнение. Так как команда MOVSW увеличивает адреса в регистрах DS и SI на 2, операция требует только пять циклов. Для процесса пересылки справа налево регистр SI должен быть инициализирован адресом NAME1+8, а регистр DI - NAME2+8.
LODS: ЗАГРУЗКА СТРОКИ ------------------------------------------------------------
Команда LODS загружает из памяти в регистр AL один байт или в регистр AX одно слово. Адрес памяти определяется регистрами DS:SI. В зависимости от значения флага DF происходит увеличение или уменьшение регистра SI. Поскольку одна команда LODS загружает регистр, то практи ческой пользы от префикса REP в данном случае нет. Часто простая команда MOV полностью адекватна команде LODS, хотя MOV генерирует три байта машинного кода, а LODS - только один, но требует инициализацию регистра SI. Можно использо вать команду LODS в том случае, когда требуется продвигаться вдоль строки (по байту или по слову), проверяя загружаемый регистр на конкретное значение. Команды, эквивалентные команде LODSB:
MOV AL,[SI] INC SI
Ассемблер для IBM PC. Глава 11 245
На рис.11.1 процедура E10LODS демонстрирует использование команды LODSW. В примере обрабатывается только одно слово: первый байт из области NAME1 (содержащий As) заносится в регистр AL, а второй байт - в регистр AH. В результате в регистре AX получится значение sA.
STOS: ЗАПИСЬ СТРОКИ ------------------------------------------------------------
Команда STOS записывает (сохраняет) содержимое регистра AL или AX в байте или в слове памяти. Адрес памяти всегда представляется регистрами ES:DI. В зависимости от флага DF команда STOS также увеличивает или уменьшает адрес в регистре DI на 1 для байта или на 2 для слова. Практическая польза команды STOS с префиксом REP - инициализация области данных конкретным значением, например, очистка дисплейного буфера пробелами. Длина области (в байтах или в cловах) загружается в регистр AX. Команды, эквивалентные команде REP STOSB:
JCXZ LABEL2 LABEL1: MOV [DI],AL INC/DEC DI ; Инкремент или декремент LOOP LABEL1 LABEL2: ...
На рис.11.1 процедура F10STOS демонстрирует использование команды STOSW. Операция осуществляет запись шест. 2020 (пробелы) пять раз в область NAME3, причем значение из регистра AL заносится в первый байт, а из регистра AH - во второй. По завершении команды регистр DI содержит адрес NAME3+10.
CMPS: СРАВНЕНИЕ СТРОК ------------------------------------------------------------
Команда CMPS сравнивает содержимое одной области памяти (адресуемой регистрами DS:SI) с содержимыми другой области (адресуемой как ES:DI). В зависимости от флага DF команда CMPS также увеличивает или уменьшает адреса в регистрах SI и DI на 1 для байта или на 2 для слова. Команда CMPS устанавливает флаги AF, CF, OF, PF, SF и ZF. При использовании префикса REP в регистре CX должна находиться длина сравниваемых полей. Команда CMPS может сравнивать любое число байт или слов.
------------------------------------------------------------ ------------------------------------------------------------ Рис. 11.1. Использование цепочечных команд.
Рассмотрим процесс сравнения двух строк, содержащих имена JEAN и JOAN. Сравнение побайтно слева направо приводит к следующему:
Ассемблер для IBM PC. Глава 11 246
J : J Равно E : O Не равно (E меньше O) A : A Равно N : N Равно
Сравнение всех четырех байт заканчивается сравнением N:N - pавно/нуль. Так как имена "не равны", операция должна пре кратиться, как только будет обнаружено условие "не равно". Для этих целей команда REP имеет модификацию REPE, которая повторяет сравнение до тех пор, пока сравниваемые элементы равны, или регистр CX не pавен нулю. Кодируется повторяющее ся однобайтовое сравнение следующим образом:
REPE CMPSB
На рис.11.1 в процедере G10CMPS имеются два примера использования команды CMPSB. В первом примере происходит сравнение содержимого полей NAME1 и NAME2. Так как ранее команда MOVSB переслала содержимое поля NAME1 в поле NAME2, то команда CMPSB продолжается на всех десяти байтах и завершается состоянием pавно/нуль: флаг SF получает значение 0 (положительно) и флаг ZF - 1(нуль). Во втором примере сравнивается поля NAME2 и NAME3. Ранее команда STOSW заполнила поле NAME3 пробелами, поэтому команда CMPB завершается после сравнения первых же байт с результатом "больше/неравно": флаг SF получает значение 0 (положительно) и флаг ZF - 0 (ненуль). Первый пример заканчивается с результатом "равно/нуль" и заносит 01 в регистр BH. Второй пример заканчивается с результатом "неравно" и заносит 02 в регистр BL. При трассировке команд с помощью отладчика DEBUG можно увидеть, что в конце процедуры G10CMPS регистр BX будет содержать значение 0102. Предупреждение! Показанные примеры используют команду CMPSB для сравнения одного байта за одно выполнение. При использовании команды CMPSW для сравнения одного слова, необходимо инициализиpовать регистр CX значением 5. Кроме того следует помнить, что команда CMPSW при сравнении слов переставляет байты. Например, сравнивая имена SAMUEL и ARNOLD команда CMPSW выбирает вместо SA и AR переставленные значения, т.е. AS и RA. В результате вместо "больше" получится "меньше", т.е. неправельный результат. Таким образом команда CMPSW работает правильно только при сравне нии строк, которые содержат числовые данные, определенные как DW, DD или DQ.
SCAS: СКАНИРОВАНИЕ СТРОК ------------------------------------------------------------
Команда SCAS отличается от команды CMPS тем, что сканирует (просматривает) строку на определенное значение байта или слова. Команда SCAS сравнивает содержимое области
Ассемблер для IBM PC. Глава 11 247
памяти (адресуемой pегистрами ES:DI) с содержимым регистра AL или AX. В зависимости от значения флага DF команда SCAS также увеличивает или уменьшает адрес в регистре DI на 1 для байта или на 2 для слова. Команда SCAS устанавливает флаги AF, CF, OF, PF, SF и ZF. При использовании префикса REP и значения длины в регистре CX команда SCAS может сканировать строки любой длины. Команда SCAS особенно полезна, например, в текстовых редакторах, где программа должна сканировать строки, выполняя поиск знаков пунктуации: точек, запятых и пробелов. На рис.11.1 процедура H10SCAS сканирует область NAME1 на строчную букву "m". Так как команда SCASB должна продолжать сканирование, пока результат сравнения - "не равно" или регистр CX не равен нулю, то используется префикс REPNE:
REPNE SCASB
Так как область NAME1 содержит слово "Assemblers", то команда SCASB находит символ "m" в пятом сравнении. При использовании отладчика DEBUG для трассировки команд в конце процедуры H10SCAS можно увидеть в регистре AH значение 03 для индикации того, что символ "m" найден. Команда REP SCASB кроме того уменьшит значение регистра CX от 10 до 06. Команда SCASW сканирует в памяти слово на соответствие значению в регистре AX. При использовании команд LODSW или MOV для пересылки слова в регистр AX, следует помнить, что первый байт будет в регистре AL, а второй байт - в регистре AH. Так как команда SCAS сравнивает байты в обратной последовательности, то oперация корректна.
СКАНИРОВАНИЕ И ЗАМЕНА ------------------------------------------------------------
В процессе обработки текстовой информации может возникнуть необходимость замены определенных символов в тексте на другие, например, подстановка пробелов вместо различных редактирующих символов. В приведенном ниже фрагменте программы осуществляется сканирование cтроки STRING и замена символа амперсанд (&) на символ пробела. Когда команда SCASB обнаружит символ & (в примере это будет позиция STRING+8), то операция сканирования прекратит ся и регистр DI будет содержать aдрес STRING+9. Для получе ния адреса символа & необходимо уменьшить содержимое DI на единицу и записать по полученному адресу символ пробела.
STRLEN EQU 15 ; Длина поля STRING STRING DB 'The time&is now' ... CLD MOV AL,'&' ;Искомый символ MOV CX,STRLEN ;Длина поля STRING LEA DI,STRING ;Адрес поля STRING REPNE SCASB ;Сканировать
Ассемблер для IBM PC. Глава 11 248
JNZ K20 ;Символ найден? DEC DI ;Да - уменьшить адрес MOV BYTE PTR[DI],20H ;Подставить пробел K20: RET
АЛЬТЕРНАТИВНОЕ КОДИРОВАНИЕ ------------------------------------------------------------
При использовании команд MOVSB или MOVSW ассемблер предполагает наличие корректной длины строковых данных и не требует кодирования операндов в команде. Для команды MOVS длина должна быть закодирована в операндах . Например, если поля FLDA и FLDB определены как байтовые (DB), то команда
REP MOVS FLDA,FLDB
предполагает повторяющуюся пересылку байтов из поля FLDB в поле FLDA. Эту команду можно записать также в следующем виде: REP MOVS ES:BYTE PTR[DI],DS:[SI]
Однако загрузка регистров DI и SI адресами FLDA и FLDB oбязательна в любом случае.
ДУБЛИРОВАНИЕ ОБРАЗЦА ------------------------------------------------------------
Команда STOS бывает полезна для установки в некоторой области oпределенных значений байтов и слов. Для дублирова ния образца, длина которого превышает размер слова, можно использовать команду MOVS с небольшой модификацией. Предположим, что необходимо сформировать строку следующего вида: ***---***---***---***---***--- . . .
Вместо того, чтобы определять полностью всю строку, можно определить только первые шесть байтов. Закодируем образец непосредственно перед обрабатываемой строкой следующим образом:
PATTERN DB '***---' DISAREA DB 42 DUP(?) . . CLD MOV CX,21 LEA DI,DISAREA LEA SI,PATTERN REP MOVSW
В процессе выполнения команда MOVSW сначала пересылает первое слово (**) из образца PATTERN в первое слово области DISAREA, затем - второе слово (*-), потом третье (--):
Ассемблер для IBM PC. Глава 11 249
***---***--- | | PATTERN DISAREA
К этому моменту регистр DI будет содержать адрес DISAREA+6, а pегистр SI - PATTERN+6, который также является адресом DISAREA. Затем команда MOVSW автоматически дублирует образец, пересылая первое слово из DISAREA в DISAREA+6, из DISAREA+2, в DISAREA+8, из DISAREA+4 в DISAREA+10 и т.д. В результате образец будет полностью продублирован по всей области DISAREA:
***---***---***---***---***--- . . . ***--- | | | | PATTERN DISAREA+6 DISAREA+12 DISAREA+42
Данную технику можно использовать для дублирования в области памяти любого образца любой длины. Образец должен быть расположен непосредственно перед принимающей областью.
ПРОГРАММА: ВЫРАВНИВАНИЕ ВПРАВО ПРИ ВЫВОДЕ НА ЭКРАН ------------------------------------------------------------
COM-программа, изображенная на рис.1.2, иллюстрирует почти весь материал, приведенный в этой главе. Процедуры программы выполняют следующие действия:
B10INPT Принимает имена длиной до 30 символов, вводимых вверху экрана. D10SCAS Использует команду SCASB для сканирования имен и об хода любого ввода, содержащего символ "звездочка". E10RGHT Использует команду MOVSB для выравнивания имен по правой границе, выводит имена в колонку в правой части экрана. Длина в поле ACTNLEN из списка параметров ввода используется для вычисления самого правого символа в имени, например:
JEROME KERN OSCAR HAMMERSTEIN RICHARD ROGERS
F10CLNM Использует команду STOSW для очистки области имени в памяти.
------------------------------------------------------------ ------------------------------------------------------------ Рис.11.2. Выравнивание вправо при выводе на экран.
ОСНОВНЫЕ ПОЛОЖЕНИЯ НА ПАМЯТЬ ------------------------------------------------------------
Ассемблер для IBM PC. Глава 11 250
ъ Для цепочечных команд MOVS, STOS, CMPS и SCAS не забы- вайте инициализировать регистр ES.
ъ Сбрасывайте (CLD) или устанавливайте (STD) флаг направ ления в соответствии с направлением обработки.
ъ Не забывайте устанавливать в регистрах DI и SI необходи мые значения. Например, команда MOVS предполагает операнды DI,SI, а команда CMPS - SI,DI.
ъ Инициализируйте регистр CX в соответствии с количеством байтов или слов, участвующих в процессе обработки.
ъ Для обычной обработки используйте префикс REP для команд MOVS и STOS и модифицированный префикс (REPE или REPNE) для команд CMPS и SCAS.
ъ Помните об обратной последовательности байтов в сравни ваемых cловах при выполнении команд CMPSW и SCASW.
ъ При обработке справа налево устанавливайте начальные адреса на последний байт обрабатываемой области. Если, например, поле NAME1 имеет длину 10 байтов, то для побайтовой обработки данных в этой области справа налево начальный адрес , загружаемый командой LEA, должен быть NAME1+9. Для бработки слов начальный адрес в этом случае - NAME1+8.
ВОПРОСЫ ДЛЯ САМОПРОВЕРКИ ------------------------------------------------------------
11.1. В данной главе приведены эквивалентные команды для а) MOVSB, б) LODSB и в)STOSB с префиксом REP. Напишите эквивалентные команды для обработки по словам а) MOVSW, б) LODSW и в) STOSW с префиксом REP.
11.2. Введите, ассемблируйте и выполните компановку программы, приведенной на рис.11.1. Не забудьте о инициализации регистра ES. Замените команды MOVSB и MOVSW для пересылки справа налево. Измените процедуру H10SCAS для сканирования поля NAME1 на слово "mb". Используя отладчик DEBUG для трассировки процедур, обратите веимание на содержимое сегмента данных и регистров.
11.3. Имеются следующие определения:
DATASG SEGMENT PARA CONAME DB 'SPACE EXPLORERS INC.' PRLINE DB 20 DUP(' ')
Используя цепочечные команды, выполните: а) пересылку данных из CONAME в PRLINE слева направо;
Ассемблер для IBM PC. Глава 11 251
б) пересылку данных из CONAME в PRLINE справа налево; в) загрузку третьего и четвертого байтов области CONAME в регистр AX; г) сохранение содержимого регистра AX в область по адресу PRLINE+5; д) сравнение данных в областях CONAME и PRLINE (они должны быть не равны); е) сканирование областей CONAME и PRLINE, и поиск в ней символа пробел. Если символ будет найден, то переслать его в регистр BH.
11.4. Переделайте процедуру H10SCAS (рис.11.1) так, чтобы выполнялось сканирование поля NAME1 на символ "er". Обратите внимание, что символы "er" не встречаются в поле NAME1 как одно слово: /As/se/mb/le/rs/. Для решения этой проблемы возможны два варианта: а) использовать команду SCASW дважды, причем первая должна начинаться по адресу NAME1, а вторая - по адресу NAME1+1; б) использовать команду SCASB для поиска символа "е" и сравнить затем следующий байт на символ "r".
11.5. Определите поле, содержащее шест.значения 03, 04, 05 и B4. Продублируйте это поле 20 раз и выдайте результат на экран.
Ассемблер для IBM PC. Глава 12 262