Chapter_09 (1110561), страница 4
Текст из файла (страница 4)
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?. . .Data ends. .
.; реализация оператора X:=A[i]movbx,i;xorax,ax; ax:=0shrbx,1; беззнаковое деление на 2moval,A[bx]; в al два элементаjcL1; i-ый элемент – правыйmovcl,4; число сдвиговshral,cl; i-ый элемент был левымL1:andal,1111b; выделение A[i]movX,alПрокомментируем эту программу. Сначала мы командой сдвига вправо разделили значение индекса i (это беззнаковое число!) на 2 и определили тот байт массива A, в котором находится параэлементов, один из которых нам нужен.
На положение нужного нам элемента в байте указывает остаток от деления индекса i на 2 в регистре CF: если остаток равен нулю (т.е. i чётное), то элементрасположен в левых четырёх битах байта, иначе – в правых. Для выделения нужного элемента, который занимает в байте только 4 бита из 8-ми, мы использовали команду логического умноженияand al,1111b , где второй операнд задан для удобства понимания смысла команды в виде двоичного числа, на что указывает буква b в конце этого числа.Использование команды логического умножения для выделения нужной нам части байта илислова, и обнуление остальной части является типичной для программирования на Ассемблере. Приэтом константа, используемая для выделения нужной части (у нас это 00001111b), называетсямаской выделения или просто маской. Таким образом, мы поместили элемент массива A, которыйзанимает 4 бита, в регистр al и дополнили этот элемент до 8 бит нулями слева.