Арифметичні дії з негативними числами

 

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

Знак результату множення чи ділення двох чисел буде негативним тоді, коли тільки одне з чисел негативне. Нехай знак першого числа записаний у старшому біті регістра R0, а знак другого числа — у старшому біті регістра R1. Тоді знак результату можна записати в біт F0 за допомогою чотирьох команд:

MOV A, R0 ; знака в старший біт нагромаджувача

XRL A, R1 ; обчислення знака результату

RLC A ; старший біт нагромаджувача в прапорці

; переносу

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

Зазначений біт зручний для запам'ятовування знака результату тому, що на нього не впливають результати команд, які виконують арифметичні операції.

В і8051 відсутня операція зміни знака числа, однак вона може бути виконана відніманням цього числа з 0. Припустимо, що нам потрібно змінити знак однобайтового числа, записаного в регістр R0. Для цього потрібно очистити біт переносу і нагромаджувач., відняти з нього вміст регістра і переслати результат з нагромаджувача в регістр:

CLR С ; очищення біта переносу

CLR А ; очищення нагромаджувача

SUBB A, R0 ; віднімання

MOV R0, А ; запам'ятовування результату

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

+ 0 00000000 - 0 00000000 – 11111111 + 1

+ 1 00000001 - 1 11111111 = 11111110 + 1

+ 2 00000010 - 2 11111110 = 11111101 + 1

З наведеного прикладу видно, що потрібно зробити для зміни знака числа на протилежний. Спочатку необхідно змінити уміст усіх розрядів вихідного числа на зворотні значення (зворотний код), а потім додати одиницю в молодший розряд числа (додатковий код). Використання цього алгоритму для зміни знака однобайтового числа не дає ніякого виграшу, якщо підлягаюче перетворенню число записане в регістр. Але для зміни знака числа, записаного в нагромаджувач, набагато простіше скористатися наступною парою команд.

CPL A ; обчислення зворотного коду

INC А ; додавання 1

Для зміни знака двобайтового числа необхідно спочатку одержати зворотні коди молодшого і старшого байтів, а потім додати 1 до молодшого байта. Нехай молодший байт числа записаний у нагромаджувачі, а старший — у регістрі В. Число зі зворотним знаком виходить у тім же місці, що і вихідне.

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

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

ADD A, #01h ; додавання 1

JNC nс ; перехід за відсутністю переносу

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

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

Зверніть увагу на те, як у випадку переносу з молодшого байта старший байт збільшується на 1. Для одержання переносу з молодшого байта потрібно використовувати в програмі не INC, a ADD. Код операції NOP записаний для того, щоб не залишати порожній рядок з міткою. У реальній програмі в цьому рядку повинна бути записана перша команда наступного блока.

Як приклад наведемо програму перемножування однобайтових чисел з довільним знаком. Вихідні числа знаходяться в регістрах R0, R1, а добуток обчислюється в регістрі В (старший байт) і нагромаджувачі (молодший байт).

MOV A, R0 ; множене

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

JNC mls1 ; перехід по позитивному множеному

CPL A ; обчислення зворотного коду множеного

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

mlsl: MOV В, R1 ; множник

JNB В.7 mls2 ; перехід по позитивному множеному

CPL С ; обчислення знака добутку

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

INC В ; обчислення модуля множника

mis2: MOV F0, С ; запам'ятовування знака добутку

MUL АВ ; обчислення добутку модулів

JNB F0, mls3 ; перехід по позитивному добутку

CPL А ; обчислення зворотного коду мол. байта XRL В #FFh ; обчислення зворотного коду ст. байта

ADD A, #01h ; додавання 1

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

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

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

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

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