9_Дополнительные воможности ассемблера (975806), страница 2
Текст из файла (страница 2)
Должно быть 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> является соответственно байтом или словом в оперативной памяти.