Среди арифметических операций над двоичными числами мы рассмотрели лишь команды сложения и вычитания. Мы применяли также операции умножения и деления. Напомним, что команда
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.