9_Дополнительные воможности ассемблера (975806), страница 4
Текст из файла (страница 4)
после выполнения команды сдвигасправедливо равенство CF=op1[N] (как мы знаем, в этом случае флаг переполнения устаналвивается OF=0). Таким образом, правильность умножения на два с помощью команды сдвига влево можноконтролировать, как обычно, анализируя флаги переноса и переполнения для беззнаковых и знаковых чисел соответственно.Заметим, что беззнаковое и знаковое умножение в нашем компьютере делается разными командами mul и imul, однако умножении на 2 с помощью команды сдвига влево делается по одному алгоритму, как для знаковых, так и для беззнаковых чисел. Именно поэтому одна команда логическогосдвига влево имеет два имени-синонима для своего кода операции: логический (т.е.
беззнаковый)сдвиг shl и арифметический (т.е. знаковый) сдвиг sal.Рассмотрим теперь команды сдвига на один разряд вправо. По аналогии со сдвигом на одинразряд влево, сдвиг на один разряд вправо можно трактовать как деление целого числа на два. Однако так как деление на два должно выполняться по разным правилам для знаковых и беззнаковых целых чисел (вспомним различные команды div и idiv), то существуют две разные команды сдвигаоперанда на один бит вправо.
Команда логического сдвига на один разряд вправоshr op1,1выполняется по правилу, которое можно так описать на Паскале:CF:=op1[0];for i:=0 to N-1 do op1[i]:=op1[i+1];1В старших моделях нашего семейства у команд сдвига дополнительно допускается также второй операндформата i8. Мы в своих примерах этой возможностью, как и договаривались, пользоваться не будем.11op1[N]:=0На рис.
9.3 показана схема выполнения этой команды.op10CFРис. 9.3. Схема выполнения команды shr op1,1.Команда логического сдвига на один разряд вправо эквивалентна делению на два беззнаковогоцелого числа, результат при этом всегда получается правильным. Делению на два знакового целогочисла в определённом смысле эквивалентна команда арифметического сдвига операнда на одинбит вправоsar op1,1она выполняется по правилу:CF:=op1[0];for i:=0 to N-1 do op1[i]:=op1[i+1]Ниже на рис. 9.4 показана схема выполнения этой команды.op1CFРис. 9.4.
Схема выполнения команды sar op1,1.Как видим, крайний левый бит аргумента при арифметическом сдвиге вправо не меняется. Притрактовке операнда сдвига как знакового числа простой анализ показывает, что для неотрицательныхи отрицательных чётных значений арифметический сдвиг вправо всегда эквивалентен операции деления этого аргумента на 2. В то же время для отрицательных нечётных значений это неверно, например, (-5) div 2 = (-2), а sar (-5),1 = (-3), т.е.
на единицу меньше. Таким образом, правильное деление на 2 сдвигом любой знаковой величины op1 реализует, например, такой фрагментпрограммы:sar op1,1jns Ladc op1,0L:Заметим далее, что, как и "настоящие" команды деления, сдвиг вправо даёт два результата: частное на месте своего операнда и остаток от деления на 2 во флаге CF. Действительно, легко видеть,чтоCF:=op1 mod 2CF:=abs(op1 mod 2)для беззнакового операнда идля знакового операнда.1Таким образом, для проверки того, является ли целое число X нечётным, можно использоватьследующие две командыshljcX,1ODD; Нечётное XПрограммисты, однако, не любят этот способ проверки на нечётность, так как при этом портитсяоперанд X.
Лучше проверять целое число X на нечётность такими двумя командамиtest X,1jneODD; Нечётное XСледующая группа команд сдвига – так называемые циклические сдвиги. Эти команды рассматривают свой операнд как замкнутый в кольцо: после бита с номером N располагается бит с номером0, а перед битом с номером 0 – бит с номером N. Ясно, что при циклическом сдвиге операнд сохраняет все свои биты, меняются только номера этих битов внутри операнда. Команда циклическогосдвига влевоrol op1,1выполняется по правилу1Как видим, есть только небольшое различие от команды знакового деления на два с кодом операцииidiv, так как остаток от деления равных по модулю делимых не различается, а одноразрадное число 1 можнотрактовать и как единицу (в беззнаковой системе) и как –1 (в знаковой системе).12shl op1,1; op1[0]:=CFДругими словами, сначала выполняется команда логического сдвига влево, а потом в результатекорректируется значение нулевого бита.
Ниже на рис. 9.5 показана схема выполнения этой команды.CFOp1Рис. 9.5. Схема выполнения команды rol op1,1.Команда циклического сдвига вправоror op1,1выполняется по правилуshr op1,1; op1[N]:=CFНиже на рис 9.6 показана схема выполнения этой команды.op1CFРис. 9.6. Схема выполнения команды ror op1,1.Команды циклического сдвига через флаг переноса включают в кольцо сдвигаемых битов дополнительный бит – флаг переноса CF, который включается между битами с номерами 0 и N. Такимобразом, в сдвиге участвуют N+1 бит. Команда циклического сдвига влево через флаг переносаrcl op1,1выполняется по правилуtemp:=CF; shl op1,1; op1[0]:=tempЗдесь t – некоторая вспомогательная (врéменная) переменная.
На рис. 9.7 показана схема выполнения этой команды.CFop1Рис. 9.7. Схема выполнения команды rcl op1,1.Команда циклического сдвига вправо через флаг переносаrcr op1,1выполняется по правилуtemp:=CF; shr op1,1; op1[N]:=tempНиже на рис. 9.8 показана схема выполнения этой команды.op1CFРис. 9.8. Схема выполнения команды rcr op1,1.Команды циклического сдвига в практике программирования используются редко – когда надопроанализировать биты операнда и в операнде можно изменять порядок этих битов.Теперь нам осталось описать команды сдвига, вторым операндом которых служит регистр cl.Каждая такая команда (КОП – любой из кодов операций команд сдвигов)КОП op1,clвыполняется по правилуtemp:=cx;for cl:=cl downto 1 do КОП op1,1 ;cx:=tempТаким образом, значение регистра cl задаёт число разрядов, на которые в цикле происходитсдвиг операнда, при этом, как видно из описания работы, сам регистр cx такими командами изме-13нить нельзя.
Кроме того, ясно, что задавать сдвиги операнда более чем на N разрядов (длину операнда в битах) не имеет большого смысла.Главное назначение логических команд – обрабатывать отдельные биты и группы битов в байтахи словах. Как мы знаем, минимальной порцией данных, отрабатываемой в командах, являются в нашей архитектуре байт, поэтому в языке машины и предусмотрены логические команды.1Разберём несколько примеров использования логических команд в программировании на Ассемблере.
Сначала составим фрагмент программы, в котором подсчитывается и выводится число битов со значением "1" во внутреннем машинном представлении переменной X размером в слово. Безиспользования логических команд это можно сделать, например, с помощью такого фрагмента программы на Ассемблере:Xdw?. . .movax,Xsubcx,cx; число "1"movbx,2L:cmpax,0jzPechmovdx,0divbxadccx,0; cx:=cx+CFjmpLPech: outint cxА теперь решим эту же задачу с использованием логических команд:Xdw?. . .movax,Xsubcx,cx; число "1"L:cmpax,0jzPechshlax,1adccx,0; cx:=cx+CFjmpLPech: outint cxЭтот алгоритм будет работать быстрее за счёт того, что медленная команда деления заменяетсяна быструю команду сдвига, кроме того, теперь мы использовали на два регистра меньше.
Заметим,что операция подсчёта числа битов машинного слова со значением "1" являлась весьма важной вархитектуре некоторых ЭВМ. В качестве примера рассмотрим отечественную ЭВМ третьего поколения БЭСМ-6, которая производилась в конце 60-х годов прошлого века [3]. В этой ЭВМ сигналыпрерывания устанавливали в "1" биты в специальном 48-разрядном регистре прерываний (каждый бит соответствовал своему номеру сигнала прерывания). В этой архитектуре существовала специальная машинная команда для подсчёта количества "1" в своём аргументе, что позволяло быстроопределить число ещё необработанных сигналов прерывания.Рассмотрим теперь использование логических команд при обработке упакованных структурданных.
Пусть, например, на языке Паскаль дано описание упакованного массива с элементами ограниченного целого типа:Const N=10000;VarA: packed array[0..N-1] of 0..15;X: byte;1По большому счёту все логические команды тоже являются избыточными в языке нашей машины, действия логических команд всегда можно заменить (довольно большими) фрагментами программ на Ассемблере,где будут использоваться команды целочисленного деления и умножения. В качестве хорошего упражненияреализуйте без использования логических команд фрагмент программы, эквивалентный, например, операторуz:=x and y , где x,y и z – переменные длиной в слово в сегменте данных.14Напомним, что служебное слово packed есть рекомендация Паскаль-машине по возможностиболее компактно хранить данные, даже если это повлечёт за собой увеличения времени доступа кэтим данным по чтению и записи.
Многие Паскаль-машины могут и "не прислушаться" к этим рекомендациям (игнорировать их), в частности, во многих случаях так поступает и знакомый Вам ТурбоПаскаль.Рассмотрим фрагмент программы на Ассемблере, который работает с описанным выше упакованным массивом A. Реализуем, например, оператор присваивания X:=A[i]. Каждый элемент массива требует для своего хранения 4 бита памяти, так что будем в одном байте хранить два последовательных элемента массива A:NDataAXequ10000segmentdbN/2 dup (?); N/2 ≡ N div 2db?.