Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Эрни Каспер Программирование на языке Ассемблер...doc
Скачиваний:
120
Добавлен:
09.11.2019
Размер:
954.88 Кб
Скачать

4.3.Арифметические действия с отрицательными числами

Команды умножения и деления дают правильные результаты только в случае положительных операндов. Поэтому для правильного вычисления произведения или частного в случае отрицательных операндов требуется сначала вычислить модули этих операндов. После умножения или деле­ния модулей в случае необходимости можно вернуться к первоначальному кодированию с учетом знака результата. Таким образом, для умножения и деления чисел, хотя бы одно из которых отрицательное, нужно знать способы вычисления модуля числа и вычисления знака результата.

Знак результата умножения или деления двух чисел будет отрица­тельным тогда, когда только одно из чисел отрицательное. Пусть знак первого числа записан в старшем бите регистра RO, а знак второго числа -- в старшем бите регистра R1. Тогда знак результата можно записать в бит FO при помощи четырех команд:

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

В i8051 отсутствует операция изменения знака числа, однако она может быть выполнена вычитанием этого числа из 0. Предположим, что нам нужно изменить знак однобайтового числа, записанного в регистр RO. Для этого нужно очистить бит переноса и накопитель, вычесть из него содержимое регистра и переслать результат из накопителя в регистр:

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

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

Для изменения знака двухбайтового числа необходимо сначала получить обратные коды младшего и старшего байтов, а затем добавить 1 к

MOV A, RO ; знака в старший бит накопителя

XRL A, R1 ;вычисление знака результата

RLC А ; старший бит накопителя в флаге переноса

MOV FO, С ; знака результата в бите FO

CLR С ; очистка бита переноса

CLR А ; очистка накопителя

SUBB A, RO ; вычитание

MOV RO, A ; запоминание результата

+0 00000000 -0 00000000 = 11111111+1

+1 00000001 -1 11111111 = 11111110+1

+2

... 00000010

......... —2

... 11111110 = 11111101+1

...........

CPL INC А А ; вычисление обратного кода ; добавление 1

младшему байту. Пусть младший байт числа записан в накопителе, а старший — в регистре В. Число с обратным знаком получается в том же месте, что и исходное.

CPL А ; вычисление обратного кода мл. байта

XRL В, #FFh ; вычисление обратного кода ст. байта

ADD A, #01h ; добавление 1

JNC nc ; переход по отсутствию переноса

INC В ; коррекция ст . байта

nc: NOP ; для записи метки

Обратите внимание на то, как в случае переноса из младшего байта старший байт увеличивается на 1. Для получения переноса из младшего байта нужно использовать в программе не INC, a ADD. Код операции NOP записан для того, чтобы не оставлять пустой строку с меткой. В реальной программе в этой строке должна быть записана первая коман­да следующего блока.

В качестве примера приведем программу перемножения однобайтовых чисел с произвольным знаком. Исходные числа находятся в регистрах RO, R1, а произведение получается в регистре В (старший байт) и накопителе (младший байт).

MOV A, RO ; множимое

MOV C, A.7 ; знак первого числа в бите С

JNC mlsl ; переход по положительному множимому

CPL A ; вычисление обратного кода множимого

INC A ; вычисление модуля множимого

mls1: MOV B, Rl ; множитель

JNB B,7 mls2 ; переход по положительному множимому

CPL С ; вычисление знака произведения

XRL В, #FFh ; вычисление обратного кода множителя

INC В ; вычисление модуля множителя

mls2: MOV FO, С ; запоминание знака произведения

MUL AB ; вычисление произведения модулей

JNB FO, mls3 ; переход по положительному произведению

CPL A ; вычисление обратного кода мл. байта

XRL B, #FFh ; вычисление обратного кода ст. байта

ADD A, #01h ; добавление 1

JNC mls3 ; переход по отсутствию переноса

INC В ; коррекция ст . байта

mls3: NOP ; для записи метки

Смысл производимых в этой программе действий должен быть понятен по ранее приведенным примерам для вычисления знака произведения и изменения знака числа. Запоминание знака числа необходимо потому, что операция умножения записывает 0 в бит переноса. При умножении и делении чисел, представленных несколькими байтами, изменение знака для вычисления модуля исходных чисел и кода результата требуют выполнения большего количества команд.

Умножение отрицательного числа на целую степень двойки при помощи сдвига ни чем не отличается от умножения положительного числа. При делении отрицательного числа нужно при каждом сдвиге записывать в старший бит старшего байта 1. Для этого после записи старшего байта числа в накопитель перед командой сдвига влево нужно скопировать седьмой бит накопителя в бит переноса.