3.3.3. Вычитание двоичных чисел без знака

Как и при анализе операции сложения, порассуждаем над сутью процессов, происходящих при выполнении операции вычитания. Если уменьшаемое больше вычитаемого, то проблем нет, – разность положительна, результат верен. Если уменьшаемое меньше вычитаемого, возникает проблема: результат меньше 0, а это уже число со знаком. В этом случае результат необходимо завернуть. Что это означает? При обычном вычитании (в столбик) делают заем 1 из старшего разряда. Микропроцессор поступает аналогично, то есть занимает 1 из разряда, следующего за старшим, в разрядной сетке операнда. Поясним на примере.

Пример 5

  05 = 00000000 00000101

 -10 = 00000000 00001010

 Для того чтобы произвести вычитание, произведем

воображаемый заем из старшего разряда:

 100000000 00000101

 -

 00000000 00001010

 =

 11111111 11111011

Тем самым по сути выполняется действие

(65 536 + 5) – 10 = 65 531,

0 здесь как бы эквивалентен числу 65 536. Результат, конечно, неверен, но микропроцессор считает, что все нормально, хотя факт заема единицы он фиксирует установкой флага переноса cf. Но посмотрите еще раз внимательно на результат операции вычитания. Это же –5 в дополнительном коде! Проведем эксперимент: представим разность в виде суммы 5 + (–10).

Пример 6

 5    = 00000000 00000101

 +

 (-10)= 11111111 11110110

 =

        1111111111111011

то есть мы получили тот же результат, что и в предыдущем примере.

Таким образом, после команды вычитания чисел без знака нужно анализировать состояние флага cf. Если он установлен в 1, то это говорит о том, что произошел заем из старшего разряда и результат получился в дополнительном коде.

Аналогично командам сложения, группа команд вычитания состоит из минимально возможного набора. Эти команды выполняют вычитание по алгоритмам, которые мы сейчас рассматриваем, а учет особых ситуаций должен производиться самим программистом. К командам вычитания относятся следующие:

dec операнд – операция декремента, то есть уменьшения значения операнда на 1;

sub операнд_1,операнд_2 – команда вычитания; ее принцип действия: операнд_1 = операнд_1 – операнд_2

sbb операнд_1,операнд_2 – команда вычитания с учетом заема (флага cf): операнд_1 = операнд_1 – операнд_2 – значение_cf

Как видите, среди команд вычитания есть команда sbb, учитывающая флаг переноса cf. Эта команда подобна adc, но теперь уже флаг cf выполняет роль индикатора заема 1 из старшего разряда при вычитании чисел.

Рассмотрим пример (листинг 2) программной обработки ситуации, разобранной в примере 6.

Листинг 2. Проверка при вычитании чисел без знака

<1> ;prg_8_4.asm

<2> masm

<3> model small

<4> stack 256

<5> .data

<6> .code ;сегмент кода

<7> main: ;точка входа в программу

<8> ...

<9> xor ax,ax

<10> mov al,5

<11> sub al,10

<12> jnc m1 ;нет переноса?

<13> neg al ;в al модуль результата

<14> m1: ...

<15> exit:

<16> mov ax,4c00h ;стандартный выход

<17> int 21h

<18> end main ;конец программы

В этом примере в строке 11 выполняется вычитание. С указанными для этой команды вычитания исходными данными результат получается в дополнительном коде (отрицательный). Для того чтобы преобразовать результат к нормальному виду (получить его модуль), применяется команда neg, с помощью которой получается дополнение операнда. В нашем случае мы получили дополнение дополнения или модуль отрицательного результата. А тот факт, что это на самом деле число отрицательное, отражен в состоянии флага cf. Дальше все зависит от алгоритма обработки. Исследуйте программу в отладчике.