assembler. Учебник для вузов_Юров В.И_2003 -637с (862834), страница 40
Текст из файла (страница 40)
В этом случае результат необходимо завернуть. Что это означает? При обычном вычитании (в столбик) делают заем 1 изстаршего разряда. Процессор поступает аналогично, то есть занимает 1 из разряда, следующего за старшим в разрядной сетке операнда. Поясним на примере.174Глава 8.
Арифметические командыПервый вариант вычитания чисел:05 = 0000000000000101-10 = 0000000000001010.Для того чтобы произвести вычитание, произведем воображаемый заем из старшего разряда:100000000 00000101000000000000101011111111 11111011.Тем самым, по сути, выполняется действие (65 536 + 5) - 10 = 65 531, 0 здеськак бы эквивалентен числу 65 536.
Результат, конечно, неверен, но процессор считает, что все нормально, тем не менее, факт заема единицы он фиксирует, устанавливая флаг переноса CF. Посмотрите еще раз внимательно на результат операциивычитания. Это же число -5 в дополнительном коде! Проведем эксперимент: представим разность в виде суммы 5 + (-10).Второй вариант вычитания чисел:5+(-10)=0000000000000101=11111111 1111011011111111 11111011.То есть мы получили тот же результат, что и в предыдущем примере. Такимобразом, после команды вычитания чисел без знака нужно анализировать состояние флага CF. Если он установлен в 1, это говорит о том, что произошел заем изстаршего разряда и результат получился в дополнительном коде.Аналогично командам сложения группа команд вычитания состоит из минимально возможного набора. Эти команды выполняют вычитание по алгоритмам,которые мы сейчас рассматриваем, а учет особых ситуаций должен производитьсясамим программистом.• Команда декремента выполняет уменьшения значения операнда на 1:dec операнд« Команда вычитания (операнд_1 = операнд_1 - операнд_2):sub операнд_1,операнд_28 Команда вычитания с учетом заема, то есть флага CF (операнд_1 = операнд_1 операнд_2 - значение_СР):sbb операнд_1,операнд_2Как видите, среди команд вычитания есть команда SBB, учитывающая флаг переноса CF.
Эта команда подобна ADC, но теперь уже флаг CF играет роль индикаторазаема 1 из старшего разряда при вычитании чисел.Рассмотрим пример (листинг 8.4) программной обработки ситуации, рассмотренной ранее для второго варианта вычитания чисел.Арифметические операции над целыми двоичными числами1 75Листинг 8.4. Проверка при вычитании чисел без знака<1><2><3><4><5><6><7>;prg_8_4.asmmasmmodel smallstack256.data.codemain:;сегмент кода; точка входа в программу<9>xor ax , ax<10>mov al,5<11>sub al,10<12>jnc ml;нет переноса?<13>neg al;в al модуль результата<14> ml: ; . .
.<15>exit:<16>mov ax,4c00h стандартный выход<17>int 21h<18>end main; конец программыВ этом примере в строке 1 1 выполняется вычитание. С указанными для этойкоманды вычитания исходными данными результат получается в дополнительном коде (отрицательный). Для того чтобы преобразовать результат к нормальному виду (получить его модуль), применяется команда NEG, с помощью которойполучается дополнение операнда. В нашем случае мы получили дополнение дополнения, или модуль отрицательного результата. А тот факт, что это на самомделе число отрицательное, отражен в состоянии флага CF.
Дальше все зависит оталгоритма обработки. Исследуйте программу в отладчике.Вычитание двоичных чисел со знакомВычитание двоичных чисел со знаком выполнять несколько сложнее. Последнийпример показал то, что процессору незачем иметь два устройства — сложения и вычитания. Достаточно наличия только одного — устройства сложения. Но для вычитания способом сложения чисел со знаком оба операнда (и уменьшаемое, и вычитаемое) необходимо представлять в дополнительном коде. Результат тоже нужнорассматривать как значение в дополнительном коде.
Но здесь возникают сложности. Прежде всего, они связаны с тем, что старший бит операнда рассматриваетсякак знаковый. Рассмотрим пример вычитания 45 - (-127).Первый вариант вычитания чисел со знаком:45 =00101101-127 = 10000001-44 =10101100.Судя по знаковому разряду, результат получился отрицательный, что, в своюочередь, говорит о том, что число нужно рассматривать как дополнение, равное-44. Правильный результат должен быть равен 172. Здесь мы, как и в случае знакового сложения, встретились с переполнением мантиссы, когда значащий разрядчисла изменил знаковый разряд операнда.
Отследить такую ситуацию можно посодержимому флага переполнения OF. Его установка в 1 говорит о том, что резуль-176Глава 8. Арифметические командытат вышел за диапазон представления знаковых чисел (то есть изменился старшийбит) для операнда данного размера и программист должен предусмотреть действияпо корректировке результата.Следующее вычитание чисел со знаком выполним способом сложения:-45 - 45 - -45 + (-45)= -90.-415 = 11010011+-45 =11010011-90 =10100110.Здесь все нормально, флаг переполнения OF сброшен в 0, а 1 в знаковом разрядеговорит о том, что значение результата — число в дополнительном коде.Вычитание и сложение операндовбольшой размерностиЕсли вы заметили, команды сложения и вычитания работают с операндами фиксированной размерности: 8, 16, 32 бита.
А что делать, если нужно сложить числабольшей размерности, например 48 битов, используя 16-разрядные операнды?К примеру, сложим два 48-разрядных числа (рис. 8.5).1-е слагаемоеt2-е слагаемое001000111001010101001011 11111000010001001000101110100101001001001-й шаг: сложение младших 16 бит 2-й шаг: сложение средних 16 бит-*(с учетом переносаиз младшего разряда):111111110011000101001001100001101 010010001011011101001011 11111000h1010010100100100Перенос в старшийразряд11110001 0001110000000000000000013-й шаг: сложение старших 16 бит(переноса из младшегоразряда нет):001000111001010111110001 000111010100010010001011Результат слежения:011010000010000011110001000111010100100010110111Рис. 8.5.
Сложение операндов большой размерностиРисунок по шагам иллюстрирует технологию сложения длинных чисел. Видно, что процесс сложения многобайтных чисел происходит так же, как и при сложении двух чисел «в столбик», — с осуществлением при необходимости переноса1 в старший разряд. Если нам удастся запрограммировать этот процесс, то мы значительно расширим диапазон двоичных чисел, над которыми можно выполнятьоперации сложения и вычитания.Принцип вычитания чисел с диапазоном представления, превышающим стандартные разрядные сетки операндов, тот же, что и при сложении, то есть использу-Арифметические операции над целыми двоичными числами177ется флаг переноса CF.
Нужно только представлять себе процесс вычитания в столбик и правильно комбинировать команды процессора с командой SB В. Чтобы написать достаточно интересную программу, моделирующую этот процесс, необходимо привлечь те конструкции языка ассемблера, которые мы еще не обсуждали.Среди прилагаемых к книге файлов в каталоге данной главы находятся исходныетексты подпрограмм, реализующих четыре основных арифметических действиядля двоичных операндов произвольной размерности1. Не поленитесь внимательно изучить их, так как они являются хорошей иллюстрацией к материалу, изучаемому в этой и последующих главах.
К этим примерам можно будет обратиться в полной мере после того, как будут изучены механизмы процедур и макрокоманд(главы 14 и 15). Кроме того, в [8] обсуждаются вопросы программирования целочисленных арифметических операций, но в расширенном контексте.В завершение обсуждения команд сложения и вычитания отметим, что кромефлагов CF и OF в регистре EFLAGS есть еще несколько флагов, которые можно использовать с двоичными арифметическими командами. Речь идет о следующихфлагах:^ ZF — флаг нуля, который устанавливается в 1, если результат операции равен О,и в 0, если результат не равен 0;Ш SF — флаг знака, значение которого после арифметических операций (и не только) совпадает со значением старшего бита результата, то есть с битом 7,15 или31 (таким образом, этот флаг можно использовать для операций над числамисо знаком).Умножение двоичных чисел без знакаДля умножения чисел без знака предназначена командаmul сомножитель_1Как видите, в команде указан всего лишь один операнд-сомножитель.
Второйоперанд-сомножитель задан неявно. Его местоположение фиксировано и зависитот размера сомножителей. Так как в общем случае результат умножения больше,чем любой из его сомножителей, то его размер и местоположение должны бытьтоже определены однозначно. Варианты размеров сомножителей и мест размещения второго операнда и результата приведены в табл. 8.2.Таблица 8.2. Расположение операндов и результата при умножении1ПервыйсомножительВторойсомножительРезультатБайтAL16 битов в АХ: AL — младшая часть результата;АН — старшая часть результатаСловоАХ32 бита в паре DX:AX: AX — младшая частьрезультата; DX — старшая часть результатаДвойное словоЕАХ64 бита в паре EDX:EAX: ЕАХ — младшая частьрезультата; EDX — старшая часть результатаВсе прилагаемые к книге файлы можно найти по адресу http://www.piter.com/download.
—Примеч. ред.1 78Глава 8. Арифметические командыИз таблицы видно, что произведение состоит из двух частей и в зависимости отразмера операндов размещается в двух местах — на месте второго сомножителя(младшая часть) и в дополнительных регистрах АН, DX, EDX (старшая часть). Как жединамически (то есть во время выполнения программы) узнать, что результат достаточно мал и уместился в одном регистре или что он превысил размерность регистра и старшая часть оказалась в другом регистре? Для этого привлекаются уже известные нам флаги переноса CF и переполнения OF:ii если старшая часть результата нулевая, то после завершения операции CF = ОnOF = 0;ш если же флаги CF и OF ненулевые, это означает, что результат вышел за пределымладшей части произведения и состоит из двух частей, что и нужно учитыватьпри дальнейшей работе.Рассмотрим следующий пример программы (листинг 8.5).Листинг 8.
5. Умножение<1> ;prg_8_5.asm<2> masm<3> model small<4> stack256<5> .data<6> rez label word<7> rez_ldb 45<8> rez_hdb 0<9> .code<10>main:<12><13><14><15><16><17><18><19><20><21><22>ml:;сегмент данных;сегмент кода;точка входа в программухог ах, ахmov al,25mul rez_ljnc ml;если нет переполнения, то на м!mov rez_h, ah ;старшую часть результата в rez_hmov rez_l,alexit:mov ax, 4c00h стандартный выходint 21hend main;конец программыВ строке 14 производится умножение значения в rez_l на число в регистре AL.Согласно информации из табл.
8.2, результат умножения будет располагатьсяв регистре AL (младшая часть) и в регистре АН (старшая часть). Для выясненияразмера результата в строке 15 командой условного перехода JNC анализируетсясостояние флага CF, и если оно не равно 1, то результат остается в рамках регистраAL. Если же CF = 1, то выполняется команда в строке 16, которая формирует в полеrez_h старшее слово результата.