9.2. Операции над неупакованными десятичными числами

Среди арифметических операций над двоичными числами мы рассмотрели лишь команды сложения и вычитания. Мы применяли также операции умножения и деления. Напомним, что команда

mul   операнд

беззнакового умножения устанавливает (DX:AX) равным произведению AX и операнда, если операндом является слово. Если операнд байтовый, то она устанавливает AX = AL*операнд. Команда

div   операнд

беззнакового деления записывает в AX частное от деления (DX:AX) на операнд, а в DX – остаток. Если операндом является байт, то в AL записывается частное от деления AX на этот байт, а в AH – остаток.

Нам понадобится также команда

adc   операнд1, операнд2,

которая складывает операнды с флагом CF и результат записывает в операнд1. Аналогично

sbb   операнд1, операнд2

вычитает из первого операнда флаг CF и второй операнд и результат записывает на место первого операнда.

Рассмотрим операции над числами в неупакованном BCD формате. При сложении кодов ASCII двух цифр младший полубайт не всегда будет цифрой.

Например, ‘5’ + ‘7’ даст результат:

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

aaa,

которая прибавляет к младшей тетраде регистра AL число 6, если младшая тетрада больше 9, а в старшую тетраду регистра AL записывает 0.

Пример 1. Рассмотрим программу сложения десятичных чисел, записанных в неупакованном BCD – формате. Числа состоят из последовательностей цифр, таких, что цифры, соответствующие младшим разрядам, находятся в начале этих последовательностей. Например, число 12345 записывается в памяти как

db  ‘54321’

В программе, начиная с младших, цифры складываются с помощью команды adc с применением команды коррекции aaa.

< 1 > title     udecadd.exe

< 2 > code     segment

< 3 > assume    cs:code, ds:code, es:code

< 4 > extrn     display:near

< 5 > main     proc  far

< 6 > push      es         ;запись

< 7 > mov       ax,0       ;адреса PSP

push  ax    ;в стек

< 1 >

< 2 > mov       bx,code    ;инициализация

< 3 > mov       ds,bx      ;регистров

< 4 > mov       es,bx      ;ds и es

< 5 > ;——сложение————————-

< 6 > clc                  ;очистить CF

< 7 > cld                  ;прямое направление

< 8 > lea       si,a

< 9 > lea       di,b

< 10 > mov       cx,10      ;10 цифр

< 11 > next:

< 12 > lodsb                ;[ds:si++]->al

< 13 > adc       al,[di]    ;сложение с переносом

< 14 > aaa                  ;коррекция ASCII

< 15 > stosb                ;al->[es:di++]

< 16 > loop      next

< 17 > ;——вывод результата——————

< 18 > lea       si,b+9     ;устанавливаем на старший разряд

< 19 > mov       cx,10

< 20 > std                  ;обратное направление

< 21 > again:

< 22 > lodsb                ;[ds:si—]->al

< 23 > add       al,’0′

< 24 > mov       dl,al

< 25 > call      display    ;вывод из al, функция 2 DOS

< 26 > loop      again

< 27 > ret

< 28 > a  db    ‘1234567890’      ;число 987654321

< 29 > b  db    ‘5432101230’      ;число 321012345

< 30 > main     endp

< 31 > code     ends

< 32 > end       main

При вычитании цифр может произойти аналогичный выход младшей тетрады из диапазона 0-9.

Например, вычитая ‘7’ – ‘9’, получаем:

и флаги CF=1, AF=1. Чтобы получить правильную цифру 8, надо от младшей тетрады (равной 14) вычесть число 6. При вычитании чисел, представленных в неупакованном BCD – формате, для обработки следующих цифр можно применить операцию sbb (вычитания с заемом). Для вычитания числа 6 из младшей тетрады регистра AL, в случае выхода за пределы 0-9, применяется команда aas. Команда aas очищает старшую тетраду.

Пример 2. Напишем программу вычитания чисел в неупакованном BCD – формате.

< 1 > title     udecsub.exe

< 2 > code     segment

< 3 > assume    cs:code, ds:code, es:code

< 4 > extrn     display:near

< 5 > main     proc  far

< 6 > push      es         ;запись сегмента PSP

< 7 > mov       ax,0       ;и нуля

< 8 > push      ax         ;в стек

< 9 > mov       bx,code

< 10 > mov       ds,bx      ;установка ds

< 11 > mov       es,bx      ;и es

< 12 > ;——вычитание неупакованных BCD чисел——

< 13 > mov       ax,0

< 14 > push      ax         ;очистка всех

< 15 > popf                 ;флагов

< 16 > cld                  ;прямое направление

lea   si,a  ;первый операнд

< 1 >

< 2 > lea       bx,u       ;вычитаемое

< 3 > lea       di,b       ;результат

< 4 > mov       cx,10      ;10 цифр

< 5 > next:

< 6 > lodsb                ;[ds:si++]->al

< 7 > sbb       al,[bx]    ;вычитание с заемом

< 8 > das                  ;коррекция ASCII

< 9 > stosb                ;al->[es:di++]

< 10 > inc       bx

< 11 > loop      next

< 12 > ;——вывод результата——————

< 13 > lea       si,b+9     ;выводим,

< 14 > mov       cx,10      ;начиная со старших

< 15 > std                  ;в обратном направлении

< 16 > again:

< 17 > lodsb

< 18 > add       al,’0′

< 19 > mov       dl,al

< 20 > call      display

< 21 > loop      again

< 22 > ret

< 23 > a  db    ‘1234567890’

< 24 > u  db    ‘5432101230’

< 25 > b  db    10 dup (‘0′)

< 26 > main     endp

< 27 > code     ends

< 28 > end       main

Команда ASCII – коррекции при умножении aam делит содержимое регистра AL на 10 и записывает частное в регистр AH, а остаток – в AL. В результате двоичное число из регистра AL, лежащее в пределах от 0 до 99, преобразуется в две десятичные цифры, из которых младшая остается в AL, а старшая цифра переходит AH.

Умножим, например, две цифры ‘5’ и ‘9’, находящиеся в регистрах AL и BL соответственно:

      and   al,0Fh   ;al->05

      and   bl,0Fh   ;bl->09

      mul   bl       ;в результате AX = 002D

      aam            ;в результате AX = 0405

      or    ax,3030h ;AH = ‘4’, AL = ‘5’

Команда ASCII – коррекции при делении aad умножает содержимое AH на 10 и число AH*10 + AL записывает в регистр AL, а в AH записывает 0. Эта команда выполняется перед операцией деления.

Пусть, например, надо разделить 48 на 8, и пусть оба числа записаны в кодах ASCII: AH = ‘4’, AL = ‘8’, BL = ‘6’. Операция деления выполняется следующим образом:

      and   bl,0Fh   ;bl->6

      and   ax,0F0Fh ;ax->0408

      aad            ;4*10+8->al

      div   bl       ;ax/bl->al

В результате al = 8.