Команды условного перехода и флаги 


Мы поможем в написании ваших работ!



ЗНАЕТЕ ЛИ ВЫ?

Команды условного перехода и флаги



Мнемоническое обозначение некоторых команд условного перехода отражает название флага, с которым они работают, и имеет следующую структуру: первым идет символ “ j ” (jump, переход), вторым — либо обозначение флага, либо символ отрицания “ n ”, после которого стоит название флага.
Такая структура команды отражает ее назначение.
Если символа “n” нет, то проверяется состояние флага, и если он равен 1, производится переход на метку перехода.
Если символ “n” присутствует, то проверяется состояние флага на равенство 0, и в случае успеха производится переход на метку перехода.
Мнемокоды команд, названия флагов и условия переходов приведены в табл. 3.
Эти команды можно использовать после любых команд, изменяющих указанные флаги.

Таблица 3. Команды условного перехода и флаги

Название флага Номер бита в eflags/flag Команда условного перехода Значение флага для осуществления перехода
Флаг переноса cf   jc cf = 1
Флаг четности pf   jp pf = 1
Флаг нуля zf   jz zf = 1
Флаг знака sf   js sf = 1
Флаг переполнения of   jo of = 1
Флаг переноса cf   jnc cf = 0
Флаг четности pf   jnp pf = 0
Флаг нуля zf   jnz zf = 0
Флаг знака sf   jns sf = 0
Флаг переполнения of   jno of = 0
       

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

Команды условного перехода и регистр ecx/cx

Архитектура микропроцессора предполагает специфическое использование многих регистров.
К примеру, регистр eax/ax/al используется как аккумулятор, а регистры bp, sp - для работы со стеком.
Регистр ecx/cx тоже имеет определенное функциональное назначение — он выполняет роль счетчика в командах управления циклами и при работе с цепочками символов. Возможно, что функционально команду условного перехода, связанную с регистром ecx/cx, правильнее было бы отнести к этой группе команд.
Синтаксис этой команды условного перехода таков:
jcxz метка_перехода (Jump if cx is Zero) — переход, если cx ноль;
jecxz метка_перехода (Jump Equal ecx Zero) — переход, если ecx ноль.

Эти команды очень удобно использовать при организации цикла и при работе с цепочками символов.
Нужно отметить ограничение, свойственное команде jcxz/jecxz. В отличие от других команд условной передачи управления, команда jcxz/jecxz может адресовать только короткие переходы — на –128 байт или на +127 байт от следующей за ней команды.

Организация циклов

Организация циклов

Цикл, как известно, представляет собой важную алгоритмическую структуру, без использования которой не обходится, наверное, ни одна программа. Организовать циклическое выполнение некоторого участка программы можно, к примеру, используя команды условной передачи управления или команду безусловного перехода jmp. Например, подсчитаем количество нулевых байтов в области mas (листинг 10.2).

Листинг 10.2. Подсчет числа нулевых элементов

<1>;prg_10_2.asm<2> model small<3>.stack 100h<4>.data<5> len equ 10;количество элементов в mas<6> mas db 1,0,9,8,0,7,8,0,2,0<7>.code<8> start:<9> mov ax,@data<10> mov ds,ax<11> mov cx,len;длину поля mas в cx<12> xor ax,ax<13> xor si,si<14> jcxz exit;проверка cx на 0, если 0, то на выход<15> cycl:<16> cmp mas[si],0;сравнить очередной элемент mas с 0<17> jne m1;если не равно, то на m1<18> inc al;в al счетчик нулевых элементов<19> m1:<20> inc si;перейти к следующему элементу<21> jmp cycl<22> exit:<23> mov ax,4c00h<24> int 21h;возврат управления операционной системе<25> end start

Цикл в листинге 10.2 организован тремя командами, jcxz, dec и jmp (строки 15 и 22). Команда jcxz выполняет здесь две функции: предотвращает выполнение «пустого» цикла (когда счетчик цикла в сх равен нулю) и отслеживает окончание цикла после обработки всех элементов поля mas. Команда dec после каждой итерации цикла уменьшает значение счетчика в регистре сх на 1. Заметьте, что при такой организации цикла все операции по его организации выполняются «вручную». Но, учитывая важность такого алгоритмического элемента, как цикл, разработчики микропроцессора ввели в систему команд группу из трех команд, облегчающую программирование циклов. Эти команды также используют регистр есх/сх как счетчик цикла. Дадим краткую характеристику этим командам:

loop метка_перехода (Loop) — повторить цикл. Команда позволяет организовать циклы, подобные циклам for в языках высокого уровня с автоматическим уменьшением счетчика цикла. Работа команды заключается в выполнении следующих действий:

  • декремента регистра ecx/cx;
  • сравнения регистра ecx/cx с нулем:
    • если (ecx/cx) > 0, то управление передается на метку перехода;
    • если (ecx/cx) = 0, то управление передается на следующую после loop команду.

loope/loopz метка_перехода (Loop till cx <> 0 or Zero Flag = 0) — повторить цикл, пока cx <> 0 или zf = 0.
Команды loope и loopz — абсолютные синонимы, поэтому используйте ту команду, которая вам больше нравиться. Работа команд заключается в выполнении следующих действий:

  • декремента регистра ecx/cx;
  • сравнения регистра ecx/cx с нулем;
  • анализа состояния флага нуля zf:
    • если (ecx/cx) > 0 и zf = 1, управление передается на метку перехода;
    • если (ecx/cx) = 0 или zf = 0, управление передается на следующую после loop команду.

loopne/loopnz метка_перехода (Loop till cx <> 0 or Not Zero flag=0) — повторить цикл пока cx <> 0 или zf = 1.
Команды loopne и loopnz также абсолютные синонимы. Работа команд заключается в выполнении следующих действий:

  • декремента регистра ecx/cx;
  • сравнения регистра ecx/cx с нулем;
  • анализа состояния флага нуля zf:
    • если (ecx/cx) > 0 и zf = 0, управление передается на метку перехода;
    • если (ecx/cx)=0 или zf=1, управление передается на следующую после loop команду.

Команды loope/loopz и loopne/loopnz пo принципу своей работы являются взаимообратными. Они расширяют действие команды loop тем, что дополнительно анализируют флаг zf, что дает возможность организовать досрочный выход из цикла, используя этот флаг в качестве индикатора. Типичное использование данных команд связано с операцией поиска определенного значения в последовательности или со сравнением двух чисел.

Недостаток команд организации цикла loop, loope/loopz и loopne/loopnz в том, что они реализуют только короткие переходы (от -128 до +127 байтов). Для работы с длинными циклами придется использовать команды условного перехода и команду jmp (см. листинг 10.2), поэтому постарайтесь освоить оба способа организации циклов. Рассмотрим несколько примеров использования команд loop, loope/ loopz и loopne/loopnz для организации циклов.

Программа из листинга 10.2 с использованием команды организации цикла будет выглядеть так, как показано в листинге 10.3.
Программа несложная; интерес в ней представляют строки 20 и 21. Команда loopnz на основании содержимого регистра сх и флага zf принимает решение о продолжении выполнения цикла. Выход из цикла происходит в одном из двух случаев: сх = 0 (просмотрены все элементы поля mas) или zf = 1 (командой cmp обнаружен нулевой элемент). Назначение следующей команды jz (строка 21) в том, чтобы распознать конкретную причину выхода из цикла. Если выход из цикла произошел после просмотра строки, в которой нет нулевых элементов, то jz не сработает, и будет выдано сообщение об отсутствии нулевых элементов в строке (строки 7, 23-25). Если выход из цикла произошел в результате обнаружения нулевого элемента, то в регистре si будет номер позиции этого элемента в поле mas, и при необходимости можно продолжить обработку. В нашем случае мы просто завершаем программу — переходим на метку exit.

Листинг 10.3. Подсчет нулевых байтов с использованием команд управления циклом

<1>;prg_10_3.asm<2> model small<3>.stack 100h<4>.data<5> len equ 10;количество элементов в mas<6> mas db 1,0,9,8,0,7,8,0,2,0<7>.code<8> start:<9> mov ax,@data<10> mov ds,ax<11> mov cx,len;длину поля mas в cx<12> xor ax,ax<13> xor si,si<14> jcxz exit;проверка cx на 0, если 0, то на выход<15> cycl:<16> cmp mas[si],0;сравнить очередной элемент mas с 0<17> jne m1;если не равно, то на m1<18> inc al;в al счетчик нулевых элементов<19> m1:<20> inc si;перейти к следующему элементу<21> loop cycl<22> exit:<23> mov ax,4c00h<24> int 21h;возврат управления операционной системе<25> end start

Заметьте, что у команды jcxz в строке 14 осталась только одна функция — не допустить выполнения «пустого» цикла, поэтому несколько изменилось ее место в тексте программы: теперь она стоит перед меткой начала цикла cycl. Изменение и контроль содержимого регистра сх в процессе выполнения каждой итерации цикла выполняет команда loop (строка 21).

Рассмотрим пример, в котором продемонстрируем типичный метод использования команды loopnz. Программа из листинга 10.4 ищет первый нулевой элемент в поле mas.

Листинг 10.4. Программа prg_10_4.asm

<1>;prg_10_4.asm<2> model small<3>.stack 100h<4>.data<5> len equ 10;количество элементов в mas<6> mas db 1,0,9,8,0,7,8,0,2,0<7> message db 'В поле mas нет элементов, равных нулю,$'<8>.code<9> start:<10> mov ax,@data<11> mov ds,ax<12> mov cx,len;длину поля mas в cx<13> xor ax,ax<14> xor si,si<15> jcxz exit;проверка cx на 0, если 0, то на выход<16> mov si,-1;готовим si к адресации элементов поля mas<17> cycl:<18> inc si<19> cmp mas[si],0;сравнить очередной элемент mas с 0<20> loopnz cycl<21> jz exit;почему вышли из цикла?<22>;вывод сообщения, если нет нулевых элементов в mas<23> mov ah,9<24> mov dx,offset message<25> int 21h<26> exit:<27> mov ax,4c00h<28> int 21h;возврат управления операционной системе<29> end start

Читатели, имеющие даже небольшой опыт программирования на языках высокого уровня, знают, что очень часто возникает необходимость использовать вложенные циклы. Самый простой пример — обработка двухмерного массива. Теперь разберемся с основными принципами организации вложенных циклов. Основная проблема, которая при этом возникает, — как сохранить значения счетчиков в регистре есх/сх для каждого из циклов. Для временного сохранения счетчика внешнего цикла на время выполнения внутреннего цикла можно использовать несколько способов: задействовать регистры, ячейки памяти или стек. Следующий фрагмент программы содержит три цикла, вложенных один в другой. Этот фрагмент можно рассматривать как шаблон для построения программы с вложенными циклами.

<1>...<2> mov ex,100;количество повторений цикла cycl_1<3> cycl_l:<4> push сх;счетчик цикла cycl_1 в стек<5>...;команды цикла cycl_1<6> mov сх,50;количество повторений цикла cycl_2<7> cycl_2:<8> push сх;счетчик цикла cycl_2 в стек<9>...;команды цикла cycl_2<10> mov cx,25;количество повторений цикла cycl_3<11> сусl_З:<12>...;команды цикла сyсl_3<13> loop cycl_3<14>...;команды цикла cycl_2<15> pop cx;восстановить счетчик цикла сусl_2<1б> loop cycl_2<17>...;команды цикла cycl_1<18> pop cx;восстановить счетчик цикла cycl_2<19> loop cycl_1<20>...

В качестве примера рассмотрим фрагмент программы, которая обрабатывает специальным образом некоторую область памяти. Область памяти рассматривается как совокупность пяти полей, содержащих 10 однобайтовых элементов. Требуется заменить все нулевые байты в этой области на значение 0ffh.

Листинг 10.5. Пример использования вложенных циклов

<1>;prg_10_5.asm<2> model small<3>.stack 100h<4>.data<5> mas db 1,0,9,8,0,7,8,0,2,0<6> db 1,0,9,8,0,7,8,0,2,0<7> db 1,0,9,8,0,7,8,0,2,0<8> db 1,0,9,8,0,7,8,0,2,0<9> db 1,0,9,8,0,7,8,0,2,0<10>.code<11> start:<12> mov ax,@data<13> mov ds,ax<14> xor ax,ax<15> lea bx,mas<16> mov cx,5<17> cycl_1:<18> push cx<19> xor si,si<20> mov cx,10<21> cycl_2:<22> cmp byte ptr [bx+si],0<23> jne no_zero<24> mov byte ptr [bx+si],0ffh<25> no_zero:<26> inc si<27> loop cycl_2<28> pop cx<29> add bx,10<30> loop cycl_1<31> exit:<32> mov ax,4c00h<33> int 21h;возврат управления операционной системе<34> end start

Комментировать листинг 10.5 нет необходимости — он очень прост для понимания.

Цепочечные команды

Цепочечные команды

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

  • 8 бит — байт;
  • 16 бит — слово;
  • 32 бита — двойное слово.

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

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

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

  • пересылка цепочки:

movs адрес_приемника,адрес_источника
movsb
movsw
movsd

  • сравнение цепочек:

cmps адрес_приемника,адрес_источника
cmpsb
cmpsw
cmpsd

  • сканирование цепочки:

scas адрес_приемника
scasb
scasw
scasd

  • загрузка элемента из цепочки:

lods адрес_источника
lodsb
lodsw
lodsd

  • сохранение элемента в цепочке:

stos адрес_приемника
stosb
stosw
stosd

  • получение элементов цепочки из порта ввода-вывода:

ins адрес_приемника,номер_порта
insb
insw
insd

  • вывод элементов цепочки в порт ввода-вывода:

outs номер_порта,адрес_источника
outsb
outsw
outsd

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

Префиксы повторения имеют свои мнемонические обозначения:

rep
repe или repz
repne или repnz

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

Отличия приведенных префиксов в том, на каком основании принимается решение о циклическом выполнении цепочечной команды: по состоянию регистра ecx/cx или по флагу нуля zf:

  • префикс повторения rep (REPeat). Этот префикс используется с командами, реализующими операции-примитивы пересылки и сохранения элементов цепочек — соответственно, movs и stos.
    Префикс rep заставляет данные команды выполняться, пока содержимое в ecx/cx не станет равным 0.
    При этом цепочечная команда, перед которой стоит префикс, автоматически уменьшает содержимое ecx/cx на единицу. Та же команда, но без префикса, этого не делает;
  • префиксы повторения repe или repz (REPeat while Equal or Zero). Эти префиксы являются абсолютными синонимами.
    Они заставляют цепочечную команду выполняться до тех пор, пока содержимое ecx/cx не равно нулю или флаг zf равен 1.
    Как только одно из этих условий нарушается, управление передается следующей команде программы. Благодаря возможности анализа флага zf, наиболее эффективно эти префиксы можно использовать с командами cmps и scas для поиска отличающихся элементов цепочек.
  • префиксы повторения repne или repnz (REPeat while Not Equal or Zero). Эти префиксы также являются абсолютными синонимами. Их действие на цепочечную команду несколько отличается от действий префиксов repe/repz. Префиксы repne/repnz заставляют цепочечную команду циклически выполняться до тех пор, пока содержимое ecx/cx не равно нулю или флаг zf равен нулю.
    При невыполнении одного из этих условий работа команды прекращается.
    Данные префиксы также можно использовать с командами cmps и scas, но для поиска совпадающих элементов цепочек.

Следующий важный момент, связанный с цепочечными командами, заключается в особенностях формирования физического адреса операндов адрес_источника и адрес_приемника.
Цепочка-источник, адресуемая операндом адрес_источника, может находиться в текущем сегменте данных, определяемом регистром ds.
Цепочка-приемник, адресуемая операндом адрес_приемника, должна быть в дополнительном сегменте данных, адресуемом сегментным регистром es.
Важно отметить, что допускается замена (с помощью префикса замены сегмента) только регистра ds, регистр es подменять нельзя.
Вторые части адресов - смещения цепочек — также находятся в строго определенных местах.
Для цепочки-источника это регистр esi/si (Source Index register — индексный регистр источника).
Для цепочки-получателя это регистр edi/di (Destination Index register - индексный регистр приемника).
Таким образом, полные физические адреса для операндов цепочечных команд следующие:

  • адрес_источника — пара ds:esi/si;
  • адрес_приемника — пара es:edi/di.

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

Последний важный момент, касающийся всех цепочечных команд, — это направление обработки цепочки. Есть две возможности:

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

Как мы увидим ниже, цепочечные команды сами выполняют модификацию регистров, адресующих операнды, обеспечивая тем самым автоматическое продвижение по цепочке. Количество байт, на которые эта модификация осуществляется, определяется кодом команды. А вот знак этой модификации определяется значением флага направления df (Direction Flag) в регистре eflags/flags:

  • если df = 0, то значение индексных регистров esi/si и edi/di будет автоматически увеличиваться (операция инкремента) цепочечными командами, то есть обработка будет осуществляться в направлении возрастания адресов;
  • если df = 1, то значение индексных регистров esi/si и edi/di будет автоматически уменьшаться (операция декремента) цепочечными командами, то есть обработка будет идти в направлении убывания адресов.

Состоянием флага df можно управлять с помощью двух команд, не имеющих операндов:
cld (Clear Direction Flag) — очистить флаг направления. Команда сбрасывает флаг направления df в 0.
std (Set Direction Flag) — установить флаг направления. Команда устанавливает флаг направления df в 1.

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

Операция пересылки цепочек

Команды, реализующие эту операцию-примитив, производят копирование элементов из одной области памяти (цепочки) в другую. Размер элемента определяется применяемой командой.

Система команд TASM предоставляет программисту четыре команды, работающие с разными размерами элементов цепочки:

movs адрес_приемника,адрес_источника (MOVe String) — переслать цепочку;
movsb (MOVe String Byte) — переслать цепочку байт;
movsw (MOVe String Word) — переслать цепочку слов;
movsd (MOVe String Double word) — переслать цепочку двойных слов.

Команда movs:

movs адрес_приемника,адрес_источника
Команда копирует байт, слово или двойное слово из цепочки, адресуемой операндом адрес_источника, в цепочку, адресуемую операндом адрес_приемника.
Размер пересылаемых элементов ассемблер определяет, исходя из атрибутов идентификаторов, указывающих на области памяти приемника и источника. К примеру, если эти идентификаторы были определены директивой db, то пересылаться будут байты, если идентификаторы были определены с помощью директивы dd, то пересылке подлежат 32-битовые элементы, то есть двойные слова.
Ранее уже было отмечено, что для цепочечных команд с операндами, к которым относится и команда пересылки movs адрес_приемника,адрес_источника, не существует машинного аналога.
При трансляции в зависимости от типа операндов транслятор преобразует ее в одну из трех машинных команд:
movsb, movsw или movsd.

Сама по себе команда movs пересылает только один элемент, исходя из его типа, и модифицирует значения регистров esi/si и edi/di. Если перед командой написать префикс rep, то одной командой можно переслать до 64 Кбайт данных (если размер адреса в сегменте 16 бит — use16) или до 4 Гбайт данных (если размер адреса в сегменте 32 бит - use32).
Число пересылаемых элементов должно быть загружено в счетчик — регистр cx (use16) или ecx (use32).

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

  • Установить значение флага df в зависимости от того, в каком направлении будут обрабатываться элементы цепочки — в направлении возрастания или убывания адресов.
  • Загрузить указатели на адреса цепочек в памяти в пары регистров ds:(e)si и es: (e)di.
  • Загрузить в регистр ecx/cx количество элементов, подлежащих обработке.
  • Выдать команду movs с префиксом rep.

На примере листинга 1 рассмотрим, как эти действия реализуются программно. В этой программе производится пересылка символов из одной строки в другую. Строки находятся в одном сегменте памяти. Для пересылки используется команда-примитив movs с префиксом повторения rep.

Листинг 1 Пересылка строк командой movs;prg_11_1.asmMASMMODEL smallSTACK 256.datasource db 'Тестируемая строка','$';строка-источникdest db 19 DUP (' ');строка-приёмник.code assume ds:@data,es:@datamain:;точка входа в программу mov ax,@data;загрузка сегментных регистров mov ds,ax;настройка регистров DS и ES ;на адрес сегмента данных mov es,ax cld;сброс флага DF — обработка строки от начала к концу lea si,source;загрузка в si смещения строки-источника lea di,dest;загрузка в DS смещения строки-приёмника mov cx,20;для префикса rep — счетчик повторений (длина строки)rep movs dest,source;пересылка строки lea dx,dest mov ah,09h;вывод на экран строки-приёмника int 21hexit: mov ax,4c00h int 21hend main

Операция сравнения цепочек

Команды, реализующие эту операцию-примитив, производят сравнение элементов цепочки-источника с элементами цепочки-приемника. Здесь ситуация с набором команд и методами работы с ними аналогична операции-примитиву пересылки цепочек.
TASM предоставляет программисту четыре команды сравнения цепочек, работающие с разными размерами элементов цепочки:

cmps адрес_приемника,адрес_источника (CoMPare String) — сравнить строки;
cmpsb (CoMPare String Byte) — сравнить строку байт;
cmpsw (CoMPare String Word) — сравнить строку слов;
cmpsd (CoMPare String Double word) — сравнить строку двойных слов.

Команда cmps

Синтаксис команды cmps:
cmps адрес_приемника,адрес_источника
Здесь:

  • адрес_источника определяет цепочку-источник в сегменте данных. Адрес цепочки должен быть заранее загружен в пару ds:esi/si;
  • адрес_приемника определяет цепочку-приемник. Цепочка должна находиться в дополнительном сегменте, и ее адрес должен быть заранее загружен в пару es:edi/di.

Алгоритм работы команды cmps заключается в последовательном выполнении вычитания (элемент цепочки-источника — элемент цепочки-получателя) над очередными элементами обеих цепочек.
Принцип выполнения вычитания командой cmps аналогичен команде сравнения cmp. Она, так же, как и cmp, производит вычитание элементов, не записывая при этом результата, и устанавливает флаги zf, sf и of.
После выполнения вычитания очередных элементов цепочек командой cmps, индексные регистры esi/si и edi/di автоматически изменяются в соответствии со значением флага df на значение, равное размеру элемента сравниваемых цепочек.
Чтобы заставить команду cmps выполняться несколько раз, то есть производить последовательное сравнение элементов цепочек, необходимо перед командой cmps определить префикс повторения.
С командой cmps можно использовать префикс повторения repe/repz или repne/repnz:

  • repe или repz — если необходимо организовать сравнение до тех пор, пока не будет выполнено одно из двух условий:
    • достигнут конец цепочки (содержимое ecx/cx равно нулю);
    • в цепочках встретились разные элементы (флаг zf стал равен нулю);
  • repne или repnz — если нужно проводить сравнение до тех пор, пока:
    • не будет достигнут конец цепочки (содержимое ecx/cx равно нулю);
    • в цепочках встретились одинаковые элементы (флаг zf стал равен единице).

Таким образом, выбрав подходящий префикс, удобно использовать команду cmps для поиска одинаковых или различающихся элементов цепочек.
Выбор префикса определяется причиной, которая приводит к выходу из цикла. Таких причин может быть две для каждого из префиксов. Для определения конкретной причины наиболее подходящим является способ, использующий команду условного перехода jcxz. Ее работа заключается в анализе содержимого регистра ecx/cx, и если оно равно нулю, то управление передается на метку, указанную в качестве операнда jcxz. Так как в регистре ecx/cx содержится счетчик повторений для цепочечной команды, имеющей любой из префиксов повторения, то, анализируя ecx/cx, можно определить причину выхода из зацикливания цепочечной команды. Если значение в ecx/cx не равно нулю, то это означает, что выход произошел по причине совпадения либо несовпадения очередных элементов цепочек.
Существует возможность еще больше конкретизировать информацию о причине, приведшей к окончанию операции сравнения. Сделать это можно с помощью команд условной передачи управления (табл. 1 и 2).

Таблица 1. Сочетание команд условной передачи управления с результатами команды cmps (для чисел со знаком)

Причина прекращения операции сравнения Команда условного перехода, реализующая переход по этой причине
операнд_источник > операнд_приемник jg
операнд_источник = операнд_приемник je
операнд_источник <> операнд_приемник jne
операнд_источник < операнд_приемник jl
операнд_источник <= операнд_приемник jle
операнд_источник >= операнд_приемник jge

Таблица 2. Сочетание команд условной передачи управления с результатами команды cmps (для чисел без знака)

Причина прекращения операции сравнения Команда условного перехода, реализующая переход по этой причине
операнд_источник > операнд_приемник ja
операнд_источник = операнд_приемник je
операнд_источник <> операнд_приемник jne
операнд_источник < операнд_приемник jb
операнд_источник <= операнд_приемник jbe
операнд_источник >= операнд_приемник jae

Как определить местоположение очередных совпавших или не совпавших элементов в цепочках?

Вспомните, что после каждой итерации цепочечная команда автоматически осуществляет инкремент/декремент значения адреса в соответствующих индексных регистрах. Поэтому после выхода из цикла в этих регистрах будут находиться адреса элементов, находящихся в цепочке после (!) элементов, которые послужили причиной выхода из цикла.
Для получения истинного адреса этих элементов необходимо скорректировать содержимое индексных регистров, увеличив либо уменьшив значение в них на длину элемента цепочки.
В качестве примера рассмотрим программу из листинга 2, которая сравнивает две строки, находящиеся в одном сегменте. Используется команда cmps. Префикс повторения - repe.

Листинг 2. Сравнение двух строк командой cmps<1>;prg_11_2.asm <2> MODEL small<3> STACK 256<4>.data<5> match db 0ah,0dh,'Строки совпадают.','$' <6> failed db 0ah,0dh,'Строки не совпадают','$'<7> string1 db '0123456789',0ah,0dh,'$';исследуемые строки<8> string2 db '0123406789','$' <9>.code<10> ASSUME ds:@data,es:@data;привязка DS и ES к сегменту данных<11> main:<12> mov ax,@data;загрузка сегментных регистров <13> mov ds,ax<14> mov es,ax;настройка ES на DS<15>;вывод на экран исходных строк string1 и string2<16> mov ah,09h <17> lea dx,string1<18> int 21h<19> lea dx,string2<20> int 21h <21>;сброс флага DF — сравнение в направлении возрастания адресов<22> cld<23> lea si,string1;загрузка в si смещения string1<24> lea di,string2;загрузка в di смещения string2 <25> mov cx,10;длина строки для префикса repe<26>;сравнение строк (пока сравниваемые элементы строк равны)<27>;выход при обнаружении не совпавшего элемента<28> cycl:<29> repe cmps string1,string2 <30> jcxz equal;cx=0, то есть строки совпадают<31> jne not_match;если не равны — переход на not_match<32> equal:;иначе, если совпадают, то <33> mov ah,09h;вывод сообщения<34> lea dx,match<35> int 21h <36> jmp exit;выход<37> not_match:;не совпали<38> mov ah,09h <39> lea dx,failed<40> int 21h;вывод сообщения<41>;теперь, чтобы обработать не совпавший элемент в строке, необходимо уменьшить значения регистров si и di<42> dec si <43> dec di<44>;сейчас в ds:si и es:di адреса несовпавших элементов<45>;здесь вставить код по обработке несовпавшего элемента<46>;после этого продолжить поиск в строке:<47> inc si <48> inc di<49> jmp cycl<50> exit:;выход<51> mov ax,4c00h <52> int 21h<53> end main;конец программы

Программа достаточно прозрачна, только два момента, на мой взгляд, требуют пояснения:



Поделиться:


Последнее изменение этой страницы: 2017-02-05; просмотров: 986; Нарушение авторского права страницы; Мы поможем в написании вашей работы!

infopedia.su Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав. Обратная связь - 18.119.131.72 (0.049 с.)