Chapter_09 (1110561), страница 2
Текст из файла (страница 2)
Таким образом, если происходит прерывание, выполнение цикла пересылки массивапрерывается, а при возврате из прерывания возобновляется с прерванного места (с команды rep). Это следуетиз того, что при прерывании запоминаются, а потом при возврате в программу восстанавливаются значениявсех регистров, используемых для правильной работы этого цикла (перечислите все эти регистры, их 8 штук!).3Такое значительное уменьшение сложности алгоритма пересылки массива при использовании цепочечных команд верно только для младших моделей нашего семейства.
В старших моделях появилась специальная4почечные команды введены в язык нашего компьютера, хотя они и являются избыточными (как мывидели, пересылку массива можно делать и без использования этих команд).Разберёмся теперь с назначением флага направления DF. Отметим сначала, что этот флаг позволяет производить пересылку массива в прямом направлении (от первого элемента к последнему)при значении DF=0 и в обратном направлении (от последнего элемента массива к его первому элементу) при DF=1, отсюда и название флага – Direction Flag (флаг направления пересылки массива).Пересылка массива в обратном направлении – не прихоть программиста, а часто единственныйспособ правильного присвоения значения одного массива другому.
Правда следует сразу сказать,что флаг направления влияет на правильность присваивания одному массиву значения другого массива только в том случае, если эти массивы перекрываются в памяти (т.е. один массив полностьюили частично занимает то же место в памяти, что и второй массив).
В качестве примера на рис. 9.1показано два случая перекрытия массивов A и B в памяти при выполнении пересылок. Из этого рисунка видно, что для случая 9.1 а) необходима пересылка в прямом направлении с флагом DF=0, адля случая 9.1 б) правильный результат присваивания массивов получается только при обратномнаправлении пересылки элементов массива с флагом DF=1. Обязательно поймите, что пересылка перекрывающихся массивов не в том направлении приведёт к ошибке.Массив ВB[1]Массив АA[1]B[2]B[3]Массив АA[1]...Массив ВB[1]A[2]A[3]...A[N-2]A[N-1]...B[N]...A[N]B[N-2]B[N-1]A[N]а). Должно быть DF=0B[N]б).
Должно быть DF=1Рис. 9.1. Два случая перекрытия массивов в памяти при пересылке.Перекрытие массивов при пересылке одного массива в другой часто встречается в практике программирования. Типичным примером является работа текстового редактора, когда при выполненииопераций вставки и удаления фрагментов текста приходится раздвигать или сжимать редактируемыйтекст. В качестве упражнения определите, в каком порядке должны пересылаться перекрывающиесяв памяти массивы символов при вставке и удалении фрагмента текста программы.Продолжим изучение строковых команд. Команды сравнения двух операндов cmpsb и cmpswимеют тот же формат память-память, что и команды movsb и movsw. Команды cmpsb и cmpsw выполняются по схеме:cmp <ds,si>,<es,di>; φ(di); φ(si)т.е. производится сравнение между собой двух коротких или длинных целых чисел, в результате чего соответствующим образом устанавливаются флаги (так же, как при выполнении обычной команды сравнения).Как мы знаем, команды сравнения обычно необходимы для правильной работы команд условного перехода.
С командами сравнения cmpsb и cmpsw удобно использовать команды-префиксы побыстродействующая область памяти – кэш, в которую автоматически помещаются, в частности, последниевыполняемые команды. Таким образом, весь наш первоначальный цикл пересылки из 5 команд будет находиться в этой кэш-памяти, скорость чтения из которой примерно на порядок больше скорости чтения из оперативной памяти. Другими словами, обращения в относительно медленную оперативную память за командами привыполнении цикла пересылки массива в старших моделях тоже производиться не будет (команды цикла будутчитаться из кэш-памяти), и сложность алгоритма также приближается к 2*N обменов. Более того, современныекомпьютеры за одно обращение к памяти читают на один байт, а сразу несколько (обычно 8 или 16) подрядрасположенных байт.
Таким образом, получается, что сложность нашего алгоритма на современных компьютерах падает уже до N/4 обменов. С памятью типа кэш мы немного познакомимся далее в нашем курсе.5вторения repe/repz (выполнять цикл, пока сравниваемые аргументы равны, т.е. флаг ZF=1) иrepne/repnz (выполнять цикл, пока сравниваемые аргументы не равны, т.е. ZF=0). Эти командыпохожи на рассмотренную ранее команду rep, но обеспечивают возможность досрочного выхода изцикла по значению флага ZF=0 (для команд repe/repz) и ZF=1 (для команд repne/repnz).В качестве примера рассмотрим следующую задачу.
Как мы знаем, строки текста во многихязыках программирования высокого уровня можно сравнивать между собой не только на равенство инеравенство, но и с помощью других операций отношения (больше, меньше и т.д.). При этом строкисчитаются упорядоченными в так называемом лексикографическом порядке (а по-простому – как всловаре). С помощью строковых команд и префиксов повторения операцию сравнения двух строкможно реализовать на Ассемблере таким образом (правда, здесь строки должны иметь одинаковуюдлину, сравнение строк разной длины реализуется несколько более сложно):NDataXYequ20000segmentdbN dup (?)dbN dup (?). . .Data endsCode segmentassume cs:Code,ds:Data,es:Data,ss:Stack.
. .movcx,N; Надо N>0 !cldleasi,Xleadi,Yrepe cmpsbjeEQ; Строки X и Y равныjbLT; Строка X меньше YjaGT; Срока X больше YВ нашем примере сравниваемые строки для простоты расположены в одном сегменте (сегментеданных), однако, как мы знаем, для строковых команд это не принципиально. Как видим, основнаячасть работы – последовательное сравнение в цикле символов двух строк до первых несовпадающихсимволов или до конца строк – выполняется двумя тесно связанными командами repe cmpsb .1Остальные строковые команды имеют формат регистр-память, но они тоже ориентированы назадачи обработки элементов строк (массивов) коротких и длинных целых чисел.
Команды сканирования строки являются командами сравнения и, при использовании в цикле, хорошо подходят дляпоиска в строке заданного короткого (scasb) или длинного (scasw) целого значения. Эти командыотличаются только битом размера аргументов w, и имеют два неявных операнда op1 и op2, гдеop1=al для w=0, и op1=ax для w=1, а второй неявный операнд op2=<es,di> является соответственно байтом или словом в оперативной памяти.
Если для краткости обозначить буквой r регистрal или ax, то схему выполнения команд сканирования строки можно записать так:cmp r,<es,di>; φ(di)Для иллюстрации использования команды сканирования напишем фрагмент программы для реализации следующей задачи: в массиве длинных целых чисел найти номер последнего нулевого элемента (пусть элементы в массиве нумеруются с единицы). Если в массиве нет нулевых элементов, тобудем в качестве ответа выдавать номер ноль, т.к.
элемента с таким номером в нашем массиве нет.Nequ120000В алгоритме работы этой программы есть небольшая тонкость, она правильно работает только при длинестрок N>0. Дело в том, что команда repe определяет цикл с предусловием, поэтому при N=0 команда сравнения cmpsb не выполняется ни разу, поэтому значение флага нуля ZF не определено и алгоритм может и не перейти на метку EQ.
В то же время часто бывает полезно сравнивать между собой и пустые строки, как в типеString языка Турбо-Паскаль (две пустые строки, конечно же, равны между собой). Для обеспечения такойвозможности можно перед командой cld поставить, например, команду cmp cx,cx , которая установит вединицу флаг ZF.6Dsegment. . .XdwN dup (?). .
.DendsCsegmentassume cs:C,ds:D,es:D,ss:StackStart:movax,Dmovds,axmoves,ax. . .movcx,N; Число элементовsubax,ax; Образец для поиска=0leasi,X+2*N-2; Адрес последнего элементаstd; Обратный просмотр эдементовrepne scaswjnzNoZero; Нет нулевых элементовinccx; Номер последнего нулевогоNoZero:outword cxЗаметим, что выход из нашего цикла возможен при попадании на нулевой элемент массива, приисчерпании счётчика цикла, а также при совпадении обоих этих условий.
Следовательно, после команд repne scasw необходимо проверить, имел ли место случай просто выхода из цикла безнахождения нулевого элемента, что мы и сделали командой условного перехода jnz NoZero .Следующими рассмотрим команды загрузки элемента строки, которые являются командами пересылки и, при использовании в цикле, хорошо подходят для эффективной последовательной загрузки на регистр коротких (lodsb) или длинных (lodsw) элементов целочисленного массива. Этикоманды отличаются только битом размера аргументов w, и имеют два неявных операнда op1 иop2, где op1=al для w=0, и op1=ax для w=1, а второй неявный операнд op2=<ds,si> являетсясоответственно байтом или словом в оперативной памяти.
Если обозначить буквой r регистр al илиax, то схему выполнения этих команд можно записать так:mov r,<ds,si>; φ(si)Ясно, что эту команду имеет смысл использовать только совместно с командами сравнения и условного перехода. В качестве примера использования команды загрузки напишем фрагмент программы для реализации следующей задачи: найти сумму тех элементов беззнакового массива коротких целых чисел, значения которых больше 100.
Если в массиве нет таких элементов, то будем вкачестве ответа выдавать число ноль.NDequ10000segment. . .XdbN dup (?). . .DendsCsegmentassume cs:C,ds:D,ss:StackStart:movax,Dmovds,ax. . .movcx,N; Число элементовsubdx,dx; Сумма=0leasi,X; Адрес первого элементаcld; Прямой просмотрL:lodsbcmpal,100jbeNoSumadddl,al; Суммирование7adcdh,0;NoSum:loop Loutword dxПрибавление CFПри суммировании коротких целых чисел мы получаем в качестве результата длинное целоечисло на регистре dx. Для упрощения алгоритма переполнение самого регистра dx не проверяется.Последними в этом формате SS рассмотрим команды сохранения элемента строки, которые являются командами пересылки и, при использовании в цикле, хорошо подходят для эффективногоприсваивания всем элементам массива заданного короткого (команда stosb) или длинного (команда stosw) целого значения.
Эти команды отличаются только битом размера аргументов w, и имеютдва неявных операнда op1 и op2, где второй неявный операнд op2=al для w=0, и op2=ax дляw=1, а первый неявный операнд op1=<es,di> является соответственно байтом или словом в оперативной памяти. Если обозначить буквой r регистр al или ax, то схему выполнения команд можнозаписать так:mov <es,di>,r; φ(di)В качестве примера использования команды сохранения напишем фрагмент программы для присваивания всем элементам массива длинных целых чисел значения единицы.NDequ30000segment.