Сборник по задачам и примерам Assembler

Читаем тут: https://remstroy31.ru/


Двоично-десятичные числа (BCD-числа)

Работай постоянно, не почитай работу для себя бедствием
или бременем и не желай себе за это похвалы и участия.
Общее благо - вот чего ты должен желать.
Марк Аврелий

Понятие о BCD-числах и элементарных действиях с ними приведены в уроке 8 «Арифметические команды» учебника. В отличие от действий с двоичными числами работа с BCD-числами в микропроцессоре реализована косвенно. В его системе команд нет инструкций, которые непосредственно выполняют основные арифметические действия над BCD-числами в том виде, как это делается для двоичных операндов. Тем более нет средств, которые каким-то образом учитывали бы знак. Все это должен делать сам программист. Ниже приведены макрокоманды, которые выполняют базовые арифметические операции над BCD-числами различной разрядности.

Неупакованные BCD-числа

Перед тем как приступать к работе с BCD-числами, необходимо договориться о том, как будут представляться BCD-числа в оперативной памяти — старший разряд BCD-числа по старшему адресу или старший разряд BCD-числа по младшему адресу. Изменить фрагмент программы для поддержки того или иного способа представления чисел не представляет особого труда. Поэтому для однозначности рассуждений выберем естественный для микропроцессоров Intel порядок следования байтов — младший байт по младшему адресу.

Сложение неупакованных BCD-чисел (макрокоманда)

add_bcdmacro summand_i.1en_l,summand_2.1 en_2,sum local ml.m2.m3
:add_bcd summand_1.1en_l,summand_2.1en_2.sum - макрокоманда
:сложения неупакованных BCD-чисел размером 1еп_1 и len_2
:байт и помещение результата в sum.
:Вход: summand_i и summand_2 - адреса младших байтов
хлагаемых; 1еп_1 и 1еп__2 - длины слагаемых в байтах.
;Выход: sum - адрес младшего байта поля суммы. Желательно.
:чтобы это поле имело длину на единицу больше, чем длина
:самого длинного слагаемого.
;Порядок следования байт - младший байт по младшему адресу (Intel).
push si
push bx
mov ax.len_l
cmp ax.len_2
jna m2
mov cx,len_l ;длина большего для сложения (см. ниже)
push ex
mov cx,len_2 ;длина меньшего для сложения (см. ниже)
push ex
mov cx.ax
lea bx.summand_l :адрес большего источника для сложения
lea si,summand_2 :адрес меньшего источника для movsb
jmp m3
т2: mov сх.1еп_2 :длина большего для сложения (см. ниже)
push ex
mov cx.len_l ;длина меньшего для сложения (см. ниже)
push ex
mov cx.len_2
lea bx.summand_2 ;адрес большего источника для сложения
lea si.summand_l :адрес меньшего источника для movsb m3: заполняем sum нулями - длина определена выше:
eld
хог al.al
lea di. sum rep stosb ;пересылка меньшего (по длине) BCD-числа в sum:
eld
push ds
pop es
lea di. sum :адрес источника см. выше
pop сх ;длина была определена выше и соотв. меньшему ВСО-числу rep movsb
pop сх ;дикл по большему
хог si,si
ml: mov al.[bx][si]
adc al, sum[si]
aaa
mov sum[si].al
inc si
loop ml
adc sum[si].O
pop bx
pop si
endm

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

Вычитание неупакованных BCD-чисел (макрокоманда)

sub_bcdmacro minuend.lenjn. deduction.len_d. difference local temp.ml.m2.exit_m
:sub_bcd minuend".len_m.deduction,len_d.difference -макрокоманда вычитания неупакованных BCD-чисел размером ;len_m и len_d байт и помещение результата в difference. ;Вход: minuend и deduction - адреса младших байтов уменьшаемого и вычитаемого: len_m и len_d - длины уменьшаемого и вычитаемого в байтах.
;Выход: difference - адрес младшего байта поля разности. :Длина поля difference должна быть не меньше длины :уменьшаемого.
;Порядок следования байт - младший байт по младшему адресу (Intel).
push si
.¦копируем уменьшаемое в difference:
push ds
pop es
eld
lea si.minuend
lea di.difference
mov cx.lenjn
push ex rep movsb
jmp ml ;копируем вычитаемое во врем, область temp:
temp db len_m dup (0)
ml: lea si .deduction
lea di,cs:temp
mov cx.len_d
push cs
pop es rep movsb
xor si.si
pop ex
m2: mov al,minuend[si]
sbb al,cs:temp[si]
aas
mov difference[si].al
inc si
1oop m2
jc m3 :на обработку заема из старшего разряда
jmp exit_m m3: пор exitjn:
pop si
end

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

Умножение неупакованных BCD-чисел (макрокоманда)

.data
k db 0 :перенос 0 < к < 255
b dw 10 ;основание системы счисления
.code
mul_bcdmacro u.i.v.j.w *
local m2.m4.m6
:mul_bcd u.i.v.j.w - макрокоманда умножения неупакованных
:BCD-чисел u и v размером i и j байт и помещение результата
:в w.
;Вход: и - адрес первого множителя; i - длина u: v - адрес
;второго множителя: j - длина v: w - адрес области
:размерностью i+j байт, куда необходимо поместить
:произведение: Ь=256 - размерность машинного слова.
:Выход: w - произведение размерностью i+j байт.
:Порядок следования байтов - младший байт по младшему адресу
:(Intel).
:сохраним регистры
push si
:очистим w
eld
push ds
pop es
xor al.al
lea di ,w
mov ex,i+j
rep stosb
xor bx.bx ;j=0..m-l
mov CX.j
m2: push ex : вложенные циклы
CiTlp v[bx].O '
je тб
:m3
xor si,si :1=0..n-1
mov cx.i
mov k.O
m4: mov al,u[si]
mul v[bx]
xor dx.dx
mov dl.w[bx+si]
add ax.dx
xor dx.dx
mov dl ,k
add ax.dx ;t-(ax) -- временная переменная
:корректируем результат - (ап)-цифра переноса: ;(а1)=результат
aam
mov k.ah
mov w[bx+si].al
:m5
inc si
loop m4
mov al.k
mov w[bx+si],al
m6: inc bx
pop ex
loop m2
pop si
endm

Нам понадобится и другой вариант этой команды — mul_bcd_r, который обрабатывает операнды с порядком следования байтов — старший байт по младшему адресу.

Деление N-разрядного беззнакового целого BCD-числа на одноразрядное BCD-число размером 1 байт (макрокоманда)

div_bcd_l_r macro u.N.v.w.r local ml
:div_bcd_l_r u.N.v.w.r - деление N-разрядного беззнакового целого BCD-числа
;на одноразрядное BCD-число размером 1 байт. :Вход: и - делимое; N - длина делимого, v - делитель. :Выход: w - частное, г - остаток. :Порядок следования байтов - старший байт по младшему адресу (не Intel) (!).
mov г.О
lea si.u ;j=0
хог di.di :j=0
mov cx.N
xor dx.dx
xor bx.bx
ml: mov ah,г
mov al.[si]
aaJ
div v сформировать результат:
mov w[di].al ;частное
mov r.ah .-остаток в г
inc si
inc di
loop ml
:если нужно - получим модуль (уберите знаки комментария)
: mov cx.N ;длина операнда
: lea bx.w
; call calc_abs_r
endm
Деление неупакованных BCD-чисел
:div_bcd_NM_r.asm - деление неупакованных BCD-чисел и и v размером n+m и п байт.
;Вход: и-и^гиз-.и,^,, - делимое: v=v1v2...vn - делитель. Ь=25б - основание системы счисления.
;Выход: q™q1q2-.qm - частное. r=rir2...rn - остаток,
:Порядок следования байт - старший байт по младшему адресу (не Intel) (!).
.data значения в и и v нужно внести
uO db 0 дополнительный байт для нормализации
u db 1,0.3.5,9,4,6 :делимое
m=$-u :длина в байтах значения и
vO db 0 :для сложения OvjV2..vn
v do 5.9.1 :делитель
n=$-v :длина в байтах значения v
ГПП1=П1-П
w db m+1 ctup (0) :для промежуточных вычислений q db mm dup (0) :частное qq db 0
г db n dup (0) :остаток
b dw 10 юснование системы счисления
d db 0
temp dw 0
temp_r db n dup (0)
borrow db 0 :факт заема на шаге D4
k db 0 ;перенос 0 =< k < 255
carry db 0
.code
:вставить описание макрокоманд calc_complement_r. mul_bcd_r. sub_bcd_r. add_bcd_r. :div_bcd_l_r
div_bcd_NM_r proc ;D1 - нормализация
xor ax.ax
mov dl.v
inc dl :vl+l
mov ax.b
div dl
mov d.al :d=b/(v1+l)
mul_bcd_r u.m.d.l.w
eld
push ds
pop es
lea si.w
lea di.uO
mov cx.m+1 rep movsb
mul_bcd_r v.n.d.l.w
eld
push ds
pop es
lea si.w+1
lea di.v
mov ex,n rep movsb :D2:
mov si.O ;n=0 :D3: @@m7: moval.uO[si]
emp v,al
jne @@ml
mov qq.9 :qq=b-l
jmp @@m2
(<t@ml: xor ax,ax
mov al.uO[si]
mul b
xor dx.dx
mov dl,uO[si+l]
add ax.dx
mov bl.v ;v->bx divbl
mov qq.al
@@m2: :проверим выполнение неравенства:
@@m4: mul v+1
mov temp. ax: taiip=v2*qq
xor ax.ax
mov al ,uO[si]
mul b
xor edx.edx
mov dl.uO[si+l] add dx.ax
mov al.qq
mul v :eax=qq*vl
sub dx,ax
xchg dx.ax
mul b
xor bx.bx
mov bl.uO[si+2]
add ax.bx
cmp temp,ax
jna (a@m3
dec qq
mov al.qq
jmp @@m4
@@m3: : D4
mul_bcd_r v.n.qq.l.w
mov bx.si
push si
sub_bcd_r uO[bx].<n+l>.w,<n+l>,uO[bx] ;v<->w
jnc @@m5 :переход, если нет заема (результат положительный)
mov borrow.1 ;запоминаем факт заема, получаем дополнение
pop si ;D5
@@m5: moval.qq
mov q[si].al
cmp borrow.1 :был заем на шаге D4 ??
jne @@m6 :D6 - компенсирующее сложение
mov borrow.0 :сбросин факт заема
dec q[si]
mov bx.si
push si
add_bcd_r uO[bx].<n+l>.vO,<n+l>,uO ;перенос не нужен :D7
@@m6: pop si
inc si
cmp si.mm
jle @@m7 :D8 - денормализация
mov bx.si
div_bcd_l_r uO[bx],N,d.r.temp_r
ret
div_bcd_NM_r endp *
main:
call div_bcd_NM_r end main

Упакованные BCD-числа
В отличие от неупакованных BCD-чисел, разработчики команд микропроцессора Intel весьма сдержанно отнеслись к проблеме обработки упакованных BCD-чисел. Существуют только две команды — DAA и DAS, которые поддерживают процесс сложения и вычитания упакованных BCD-чисел. Умножение и деление этих чисел не поддерживается вовсе. По этой причине при необходимости выполнения арифметических вычислений с упакованными BCD-числами есть смысл предварительно преобразовывать их в неупакованное представление, выполнять необходимые действия, результат которых конвертировать (если нужно) обратно в упакованный вид. Так как действия по преобразованию не являются основными в процессе вычислений, то желательно, чтобы они были максимально быстрыми. Можно предложить много вариантов подобного преобразования. Рассмотрим один из них.

Преобразование упакованного BCD-числа размером N байт в неупакованное BCD-число (макрокоманда)

BCD_PACK_TO_UNPACK macro PACK.N. UNPACK local cycl
;BCD_PACK_TO_UNPACK PACK,N.UNPACK - макрокоманда преобразования упакованного BCD-числа размером N байт в неупакованное BCD-число размером N*2 байт ;Порядок следования байтов - младший байт по младшему адресу :(Intel).
сохраняем регистры ...
push ds
pop es
mov ecx.N
eld ;порядок обработки BCD-цифр - начиная с младшей
lea edi.UNPACK
lea esi.PACK cycl: xorax.ax
lodsb ;загрузить очередные 2 упакованные BCD-цифры из PACK в al
гог ах.4
гог ah.4
stosw
loop cycl восстанавливаем регистры ...
endm

Преобразование неупакованного BCD-числа размером N байт в упакованное BCD-число (макрокоманда)

ВС D_U N РАС К_ТО_РАСК macro UNPACK.N,PACK local cycl
:BCD_UNPACK_TO_PACK UNPACK,N.PACK - макрокоманда преобразования неупакованного
iBCD-числа"размером N байт в упакованное BCD-число. ;Порядок следования байтов - младший байт по младшему адресу ;(Intel).
сохраняем регистры ... push ds
pop es
mov ecx.N
:определяем N/2 (размерность PACK) - если нечетное, юкругляем в большую сторону
shr ecx.l ;делим на 2
bt есх.О
jc $+4
setc Ы
inc есх добавляем 1 для округления в больщую сторону предыдущие три команды можно заменить одной: adcecx.O :теперь в есх правильное значение сч. цикла в соответствии с размерностью UNPACK
eld шорядок обработки BCD-цифр - начиная с младшей
lea edi.PACK
lea esi.UNPACK cycl: xorax.ax загрузить очередные 2 неупакованные BCD-цифры из UNPACK в ах
lodsw
rol ah,4
rol ax,4
stosb
loop cycl
emp . 0
jne $+7
and byte ptr [edi-1].OfOh восстанавливаем регистры ...
endm


Книжный магазин