Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
прогр.docx
Скачиваний:
29
Добавлен:
21.12.2018
Размер:
2.58 Mб
Скачать
  1. От типа операнда в команде безусловного перехода (ближний или дальний);

  2. от указания перед адресом перехода (в команде перехода) модификатора; при этом сам адрес перехода может находиться либо непосредственно в команде (прямой переход), либо в регистре или в ячейке памяти (косвенный переход).

Модификатор может принимать следующие значения:

near ptr – прямой переход на метку внутри текущего сегмента кода (изменяется только содержимое регистра ip);

far ptr – прямой переход на метку в другом сегменте кода (изменяется содержимое регистров cs и ip);

word ptr – косвенный переход на метку внутри текущего сегмента кода (изменяется только содержимое регистра ip);

dword ptr – косвенный переход на метку в другом сегменте кода (изменяется содержимое регистров cs и ip);

Безусловный переход jmp

Общий синтаксис:

Jmp [модификатор] адрес_перехода – безусловный переход без сохранения точки возврата.

Существуют несколько кодов машинных команд безусловного перехода jmp.

Дальность перехода определяется местоположением операнда адрес_перехода и бывает внутрисегментным или близким или межсегментным или дальним.

Можно выделить три варианта внутрисегментного использования команды jmp:

прямой короткий (расстояние от команды jmp до адреса перехода не более чем -128 или +128 байтов, машинная команда безусловного перехода длиной в 2 байта);

прямой (машинная команда безусловного перехода длиной в 3 байта);

косвенный.

Если адрес перехода расположен до команды jmp, то ассемблер формирует короткую команду безусловного перехода без дополнительный указаний. Если адрес перехода располагается после команды jmp, то для формирования короткого безусловного перехода используют модификатор short ptr.

Пример 1: (формирование короткого безусловного перехода):

jmp short ptr m1

; не более 35-40 команд (127 байтов)

m1:

Пример 2: (формирование короткого безусловного перехода):

m1:

; не более 35-40 команд (127 байтов)

jmp m1

При прямом внутрисегментном переходе можно производить переходы в приделах 64 Кбайт относительно следующей за jmp команды.

Пример 3:

M1:

; расстояние более 128 байт и менее 64 Кбайт

jmp m1

.

При косвенном внутрисегментном переходе указывается не сам адрес перехода, а место, где он записан.

Пример 4:

Lea bx,m1

Jmp bx ; адрес перехода в регистре bx

m1:

Пример 5:

.data

addr dw m1

dw m2

.code

cycl:

mov si,0

jmp addr[si] ; адрес перехода в слове addr+(si)

mov si,1

jmp addr[si] ; адрес перехода в слове addr+(si)

jmp cycl

m1:

m2:

Межсегментный переход изменяет регистры cs и eip/ip. Межсегментный переход поддерживает два варианта команд безусловного перехода: прямой и косвенный.

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

Пример 6:

Seg_1 segment

jmp far ptr m2

m1 label far

seg_1 ends

seg_2 segment

m2 label far

jmp m1

Команда косвенного межсегментного перехода в качестве операнда имеет адрес области памяти , в которой содержатся смещение и сегментная часть адреса перехода.

Таким образом, модификаторы short ptr, near ptr, word ptr применяются для организации внутрисегментных переходов, а far ptr, dword ptr – межсегментных.

Процедуры

В языке ассемблера есть несколько средств, решающих проблему дублирования участков программного кода. К ним относятся:

– механизм процедур;

– макроассемблер;

– механизм прерываний.

Обратиться к процедуре можно с помощью любой команды перехода.

Контекст – информация о состоянии программы в точке вызова процедуры.

Есть две команды, осуществляющие работу с контекстом. Это команды call и ret:

call[модификатор] имя_процедуры – вызов процедуры. Команда call, подобно jmp передаёт управление по адресу с символическим именем имя_процедуры, но при этом в стеке сохраняется адрес возврата. Адрес возврата – это адрес команды, следующей после команды call;

ret [число] – возвратить упраление вызывающей программе. Команда ret считывает адрес возврата из стека и загружает его в регистр cs и eip/ip тем самым возвращая управление на команду следующую в программе за командой call. [число] – необязательный параметр, обозначающий количество элементов, удаляемых из стека при возврате из процедуры.

Вызов процедуры командой call может быть:

внутрисегментным – процедура находиться в текущем сегменте кода (имеет тип near), и в качестве возврата команда call сохраняет в стек только содержимое регистра eip/ip;

межсегментным – процедура находится в другом сегменте кода (имеет тип far) и для осуществления возврата команда call заносит в стек содержимое обоих регистров cs и eip/ip. Очерёдность такова: сначала в стек помещается регистр cs, а потом eip/ip.

Подобно команде jmp существует четыре разновидности команды call, в зависимости от значения [модификатор] и атрибута дальности в описании процедуры.

Значения [модификатор] такие же, как и у команды jmp за исключением short ptr.

– near ptr;

а) прямой короткий (short ptr);

б) прямой;

в) косвенный.

– far ptr;

– word ptr

– dword ptr

Условные переходы

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

отношение между операндами со знаком (больше-меньше);

отношение между операндами без знака (выше-ниже);

состояние арифметических флагов zf,sf, cf, of, pf.

Команды условного перехода имеют одинаковый синтаксис:

Jcc метка_перехода

Межсегментной передачи управления в условных переходах не допускается (максимальный переход равен размеру сегмента).

Источниками условия для перехода могут быть:

любая команда, изменяющая состояние арифметических флагов;

команда сравнения cmp;

состояние регистра ecx/cx.

Команда cmp выполняет вычитание операторов и устанавливает флаги, не записывает результат вычитания на место первого операнда.

Cmp операнд_1, операнд_2

Значения аббревиатур в названии команды jcc:

Пример 7: сравним очередной элемент массива с 5:

Команды условного перехода и флаги

cmp x, y

Опкод

Значение(переход,если...)

Условие

JA

Jump if above (X > Y)

CF=0 & ZF=0

JAE

Jump if above or equal (X >= Y)

CF=0

JB

Jump if below (X < Y)

CF=1

JBE

Jump if below or equal (X < Y)

CF=1 or ZF=1

JC

Jump if carry (cf=1)

CF=1

JCXZ

Jump if CX=0

регистр CX=0

JE (то же, что и JZ)

Jump if equal (X = Y)

ZF=1

JG

Jump if greater (signed) (X > Y)

ZF=0 & SF=OF

JGE

Jump if greater or equal (signed) (X >= Y)

SF=OF

JL

Jump if less (signed) (X < Y)

SF != OF

JLE

Jump if less or equal (signed) (X <= Y)

ZF=1 or SF!=OF

JMP

Безусловный переход

-

JNA

Jump if not above (X <= Y)

CF=1 or ZF=1

JNAE

Jump if not above or equal (X < Y)

CF=1

JNB

Jump if not below (X >= Y)

CF=0

JNBE

Jump if not below or equal (X > Y)

CF=1 & ZF=0

JNC

Jump if not carry (cf=0)

CF=0

JNE

Jump if not equal (X != Y)

ZF=0

JNG

Jump if not greater (signed) (X <= Y)

ZF=1 or SF!=OF

JNGE

Jump if not greater or equal (signed) (X < Y)

SF!=OF

JNL

Jump if not less (signed) (X >= Y)

SF=OF

JNLE

Jump if not less or equal (signed) (X > Y)

ZF=0 & SF=OF

JNO

Jump if not overflow (signed) (of=0)

OF=0

JNP

Jump if no parity (pf=0)

PF=0

JNS

Jump if not signed (signed) (sf=0)

SF=0

JNZ

Jump if not zero (X != Y)

ZF=0

JO

Jump if overflow (signed) (of=1)

OF=1

JP

Jump if parity (pf=1)

PF=1

JP

Jump if parity (pf=1)

PF=1

JPE

Jump if parity even

PF=1

JPO

Jump if parity odd

PF=0

JS

Jump if signed (signed)

SF=1

JZ

Jump if zero (X = Y)

ZF=1

Пример 8: надо преобразовать строчные буквы в прописные. Строчным буквам в таблице ASCII символов соответствует диапазон 61h-7ah, прописным – 41h-5ah. Для выполнения преобразования между строчными и прописными буквами достаточно инвертировать 5 бит.

Например:

а – 01100001 z – 01111010

А – 01000001 Z – 01011010

Таблица ASCII кодов.

0

1

2

3

4

5

6

7

8

9

A

B

C

D

E

F

0

...

...

0

@

P

'

Р

А

Р

а

...

...

...

р

Ё

1

...

...

!

1

A

Q

a

Q

Б

С

б

...

...

...

с

ё

2

...

...

"

2

B

R

b

R

В

Т

в

...

...

...

т

Є

3

...

...

#

3

C

S

c

S

Г

У

г

...

...

...

у

є

4

...

...

$

4

D

T

d

T

Д

Ф

д

...

...

...

ф

Ї

5

...

...

%

5

E

U

e

U

Е

Х

е

...

...

...

х

ї

6

...

...

&

6

F

V

f

V

Ж

Ц

ж

...

...

...

ц

Ў

7

...

...

'

7

G

W

g

W

З

Ч

з

...

...

...

ч

ў

8

...

...

(

8

H

X

h

X

И

Ш

и

...

...

...

ш

°

9

...

...

)

9

I

Y

i

Y

Й

Щ

й

...

...

...

щ

A

...

...

*

:

J

Z

j

Z

К

Ъ

к

...

...

...

ъ

·

B

...

...

+

;

K

[

k

{

Л

Ы

л

...

...

...

ы

C

...

...

,

<

L

\

l

|

М

Ь

м

...

...

...

ь

D

...

...

-

=

M

]

m

}

Н

Э

н

...

...

...

э

¤

E

...

...

.

>

N

^

n

~

О

Ю

о

...

...

...

ю

F

...

...

/

?

O

_

o

¤

П

Я

п

...

...

...

я

Команды условного перехода и регистр есх/сх.

Регистр есх/сх выполняет роль счётчика в командах управления циклами и при работе с цепочками символов.

Синтаксис этой команды таков:

Jcxz метка_перехода (Jump if is zero) – прыжок, если сх ноль;

jecxz метка_перехода (Jump equal ecx zero) – переход, если есх ноль.

В отличие от других команд условной передачи управления, команды Jcxz/ jecxz могут адресовать только короткие переходы – на -128 байт или на +127 байт от следующей за ней командой.

Организация циклов

В системе команд микропроцессора существует три команды для работы с циклами. Данные команды:

1) loop метка_перехода – повторить цикл. Работа команды заключается в выполнении следующих действий:

а) декремента регистра есх/сх;

б) сравнение регистра есх/сх с нулём;

если (есх/сх)>0, то управление передаётся на метку перехода;

если (есх/сх)=0, то управление передаётся на следующую после loop команду;

2) loope/loopz метка_перехода – повторить цикл пока сх<>0 или zf=0. Команды loope и loopz – абсолютные синонимы. Работа команд заключается в выполнении следующих действий:

а) декремента регистра есх/сх;

б) сравнение регистра есх/сх с нулём;

в) анализа состояния флага нуля zf;

если (есх/сх)>0, и zf=1, управление передаётся на метку перехода;

если (есх/сх)=0, или zf=0, управление передаётся на следующую после loop команду;

3) loopne/loopnz метка_перехода – повторить цикл пока сх<>0 или zf=1. Команды loope и loopz – абсолютные синонимы. Работа команд заключается в выполнении следующих действий:

а) декремента регистра есх/сх;

б) сравнение регистра есх/сх с нулём;

в) анализа состояния флага нуля zf;

если (есх/сх)>0, и zf=0, управление передаётся на метку перехода;

если (есх/сх)=0, или zf=1, управление передаётся на следующую после loop команду;

Команды loop loope/loopz и loopne/loopnz реализуют только короткие переходы (от -128 до _127 байтов). Для работы с длинными циклами необходимо использовать команду jmp.

При работе с вложенными циклами возникает проблема сохранения значения счётчика внешнего цикла есх/сх на время выполнения внутреннего цикла. Для этого можно использовать регистры, ячейки памяти или стек.

Пример программы, содержащей три цикла вложенных один в другой.

Сложение двоичных чисел без знака

Если при сложении результат превышает размерность поля операнда, то результат оказывается неверным. Например: 254+5=259 (11111110+0000101=1 00000011). Для фиксирования ситуации выхода за разрядную сетку результата предназначен флаг переноса cf. Поэтому после операции сложения в программе предполагается включение участков кода, в которых анализируется флаг cf. Это можно сделать с помощью команды условного перехода jc <метка>.

В системе команд микропроцессора имеются три команды двоичного сложения:

INC <операнд> ; операция инкремента, то есть увеличение значения операнда на 1.

ADD <операнд_1><операнд_2>;

Принцип действия:

операнд_1=операнд_1+операнд_2.

В этой команде нельзя использовать переменную одновременно и для опернда_1, и для операнда_2. Команда ADD не различает числа со знаком и без знака.

ADC <операнд_1><операнд_2>; команда сложения с учётом флага переноса cf.

Принцип действия:

операнд_1=операнд_1+операнд_2+значение_cf.

Имеющиеся варианты приёмника и источник:

Операнд_1

Операнд_2

пример

регистр

регистр

add ecx, edx

регистр

память

add ecx, dword ptr [104h]/add ecx, [edx]

регистр

значение

add eax, 102

память

значение

add dword ptr [401231h], 80

память

регистр

add dword ptr [401231h], edx

Сложим, например, два 64-битных целых числа: одно число находится в регистрах EDX:EAX, а другое в регистрах ЕВХ:ЕСХ:

add eax,ecx

adc edx,ebx

Если при сложении младших двойных слов произошел перенос из старшего разряда (флаг CF=1), то он будет учтен следующей командой ADC.

Вычислим сумму 254+17 в регистр ах. При этом известно, что результат сложения выходит за границы операнда.

– XADD <операнд_1><операнд_2>; выполняет команду «обменять между собой и сложить».

Выполняет сложение, помещает содержимое операнд_1 в операнд_2, — сумму операндов — в операнд_1. Операнд_2 всегда регистр, операнд_1 может быть регистром и переменной.

Сложение двоичных чисел со знаком

Флаг переноса cf, установка которого в 1 говорит о том, что произошёл выход за пределы разрядности операндов. Регистрация состояния старшего (знакового) разряда операнда осуществляется с помощью флага переполнения of в регистре eflags (бит 11).

Пример1.

30566=0111011101100110

+

00687=0000001010101111

=

31253=0111101000010101 – результат правильный.

Пример2.

30566=0111011101100110

+

30566=0111011101100110

=

61132=1110111011001100 – результат неправильный, значение получилось отрицательное (в старшем бите 1).

Пример3.

-30566=1000100010011010

+

-04875=1110110011110101

=

-35441=0111010110001111 – результат неправильный, так как получилось положительное число (в старшем бите 0)

Пример4.

-4875=1110110011110101

+

-4875=1110110011110101

=

-9750=1101100111101010 – результат правильный.

Таким образом, ситуация переполнения (установка флага of в 1) происходит при переносе:

  1. из 14-го разряда (для положительных чисел);

  2. из 15-го разряда (для отрицательных чисел).

Переполнения не происходит (флаг of сбрасывается в 0), если есть перенос из обоих разрядов или перенос отсутствует в обоих разрядах.

Правильный процесс сложения чисел со знаком и без знака необходимо проводить, анализируя флаги cf и of. Состояние флагов после выполнения команд ADD, ADC, INC сведено в табл. 1.

Таблица 1.

Пример: найдём сумму чисел -6+7. Для чисел отведено 2 байта. Для этого -6 представим в дополнительном коде: 0000000000000110 инвертируем, получим:

1111111111111001

+

0000000000000001

=

11111111111110102 =6553010 =fffa16

Тогда:

mov ax,FFFAh

mov dx, 7h

add ax,dx

fffa+7=1 0001 – результат не помещается в регистр АХ, будет установлен флаг переноса cf в 1. Если значение cf проигнорировать, то оставшееся в АХ значение будет правильным.

Пример: вычтем 25-300

0000000000011001

-

0000000100101100

=

11111110111011012 =6526110 = -27510

Если произошёл заём 1 из старшего разряда, то состояние флага cf установлено в 1 – значит результат получился в дополнительном коде.

1111111011101101 инвертируем 0000000100010010

+ 0000000000000001

= 00000001000100112 = 27510

При выполнении команды сложения устанавливаются следующие флаги:

1) CF=1 , если результат не помещается в приемнике, т.е. произойдет перенос.

2) PF=1; если результат имеет четное число битов с 1.

3) AF=1, если результат сложения десятичных чисел требует коррекции.

4) ZF=1, если результат =0.

5) SF= 1, если результат отрицателен.

6) OF=1, если при сложении 2-х чисел одного знака результат превышает диапазон допустимых значений в обратном коде, а сам приемник меняет знак. SF и OF имеют смысл при сложении чисел со знаком, AF – для десятичных чисел.

Вычитание двоичных чисел без знака

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

К командам вычитания относят:

DEC <операнд> – операция декремента. Флаг CF не затрагивается, а остальные арифметические флаги (OF, SF, ZF, AF, PF) устанавливаются в соответствии с результатом вычитания.

SUB <операнд_1>,<операнд_2> – команда вычитания;

Принцип действия:

операнд_1=операнд_1-операнд_2.

Нельзя использовать переменную одновременно и для операнд_2, и для операнд_1. Команда SUB не делает различий между числами со знаком и без знака.

SBB <операнд_1>,<операнд_2> – команда вычитания с учётом заёма (флага cf).

Принцип действия:

операнд_1=операнд_1-операнд_2-значение_cf.

Пример: вычтем из одного 64-битного числа, хранящегося в EDX:EAX другое, хранящееся в ЕВХ:ЕСХ.

sub eax,ecx

sbb edx,ebx

Если при вычитании младших двойных слов произошел заем, то он будет учтен при вычитании старших.

Пример: в регистр al поместить результат вычитания 5-10.

0000 0101

-

0000 1010

-----------------

1111 1011=FFFBh

0-1111 1011=0000 0101(neg)

Факт того, что в al записано отрицательное число, отражён в состоянии флага cf.

Вычитание двоичных чисел со знаком

Вычитание в микропроцессоре производится путём сложения. Результат вычитания нужно рассматривать как значение в дополнительном коде. Например:

-45-45=-90

-45=11010011

+

-45=11010011

=

-90=10100110 число в дополнительном коде.

01011001

+00000001

=010110102 =9010

Умножение двоичных чисел без знака

Для умножения чисел без знака предназначена команда:

MUL <источник>;

Выполняет умножение содержимого источника (регистр или переменная) и регистра AL, АХ, ЕАХ (в зависимости от размера источника) и помещает результат в АХ, DX:AX, EDX:EAX соответственно. Произведение имеет двойной размер.

Размер Источник Результат

Если АН, DX, EDX содержит только нули, то флаги CF=0 и OF=0, иначе — в 1.

Например, умножим содержимое регистра al (25) на 45.

Умножение двоичных чисел со знаком

IMUL <операнд_1>[, операнд_2, операнд_3]; умножение чисел со знаком.

Эта команда имеет три формы, различающиеся числом операндов:

1) IMUL <операнд_1>: операнд_1 (регистр или переменная) умножается на AL, АХ или ЕАХ (в зависимости от размера операнда), и результат располагается в АХ, DX:AX или EDX:EAX соответственно.

2) IMUL <операнд_1>,<операнд_2>: операнд_2 (число, регистр или переменная) умножается на операнд_1 (регистр), и результат заносится в операнд_1.

3) IMUL <операнд_1>,<операнд_2>,<операнд_3>: операнд_2 (регистр или переменная) умножается на операнд_3 (число), и результат заносится в операнд_1 (регистр).

Произведение имеет двойной размер

В первом случае приемник (регистры АХ, DX:AX или EDX:EAX) всегда больше операнд_1.

Если результат мал и умещается в одном регистре (то есть если cf=of=0), то содержимое другого регистра (старшей части) является расширением знака – все его биты равны старшему биту (знаковому разряду) младшей части результата.

В противном случае (если cf=of=1) знаком результата является знаковый бит старшей части результата, а знаковый бит младшей части является значащим битом двоичного кода результата.

Пример: mul bl ;al*bl, результат в Ах.

Пример: mul bx ;ax*bx, результат Dx:Ax.

Деление двоичных чисел без знака.

Для деления чисел без знака предназначена команда:

DIV <делитель> ;

Команда выполняет целочисленное деление без знака АХ, DX:AX или EDX:ЕАХ (в зависимости от размера делителя) на делитель (регистр или переменная) и помещает результат в AL, АХ или ЕАХ, а остаток — в АН, DX или EDX соответственно.

Расположение операндов и результата при делении

размер делителя

деление

частное в...

остаток в...

BYTE (8-bits)

ax / делитель

AL

AH

WORD (16-bits)

dx:ax* / делитель

AX

DX

DWORD (32-bits)

edx:eax* / делитель

EAX

EDX

Пример: div bl ;ax/bl, результат в Al,Ah.

Пример: div bx; Dx:Ax/Bx, результат Ax,Dx.

Источник может быть:

  1. 8-бит регистр (al, ah, cl,...)

  2. 16-бит регистр (ax, dx, ...)

  3. 32-бит регистр (eax, edx, ecx...)

  4. 8-бит значение из памяти (byte ptr [xxxx])

  5. 16-бит значение из памяти (word ptr [xxxx])

  6. 32-бит значение памяти (dword ptr [xxxx])

Прерывание 0 – «деление на 0» – при выполнении команды div может возникнуть по одной из следующих причин:

  1. делитель равен 0;

  2. частное не входит в отведённую под него разрядную сетку, что может случиться в следующих случаях:

– при делении слова на байт значение делимого в более чем 256 раз больше значения делителя;

– при делении двойного слова на слово делимое в более чем 65536 раз больше значения делителя;

– при делении учетверённое слова на двойное слово делимое в более чем 4 294 967 296 раз больше значения делителя.

Пример:

DIV BX ; Dx:Ax/Bx, результат Ax,Dx.

DIV MEM_BYTE ; AX/MEM_byte, результат Al,Ah

Пример1: необходимо выполнить деление 514/2, D=2d=2h, C=514d=202h.

.data

C dw 514

D db 2

.code

mov ax,C

НЕПРАВИЛЬНО!!!

mov bl,D

div bl ; Ax/Bl; 10 0000 0010/10; 2d*256d=512d<=514d

2*256=512<514

Пример2: необходимо выполнить деление 2 293 830/35, D=35d=23h, C=2293830d=0023 0046h.

.data

C dd 2 293 830 ; 00230046h

D dw 35 ; 0023h

.code

mov eax, C ; ax=0046h

НЕПРАВИЛЬНО!!!

mov dx, word ptr [C+2] ; dx=0023h

НЕПРАВИЛЬНО!!!

mov bx,D

div bx ; Dx:Ax/Bx=00230046h/0023h; 35d*65536d=2 293 760d<2 293 830d

35*65536=2 293 760<2 293 830

Деление двоичных чисел со знаком

Для деления чисел со знаком предназначена команда:

IDIV <делитель> ;

Команда выполняет целочисленное деление со знаком АХ, DX:AX или EDX:ЕАХ (в зависимости от размера делителя) на делитель (регистр или переменная) и помещает результат в AL, АХ или ЕАХ, а остаток — в АН, DX или EDX соответственно.

Для этой команды верно всё то же, что и для команды DIV.

Лекция №4. Арифметические операции над двоично-десятичными числами **********************

В приложениях, в которых числа должны быть большими и точными необходимо использовать BCD –числа. Отдельных команд сложения, вычитания, умножения и деления BCD-чисел нет. Складывать и вычитать можно BCD-числа как в упакованном формате, так и в неупакованном, а вот делить и умножать можно только неупакованные BCD-числа.

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

Пример1: Результат сложения не больше 9. 6+3

6=0000 0110

+

3=0000 0011

=

9=0000 1001 – результат правильный.

Пример2: Результат сложения больше 9.

06=0000 0110

+

07=0000 0111

=

13=0000 1101 – результат неправильный. Правильный результат в неупакованном BCD-формате должен быть таким 0000 0001 0000 0011. В десятичном представлении это 13.

Специальных команд для работы с BCD-числами нет, но есть корректировочные команды. Для коррекции операции сложения двух однозначных неупакованных BCD-чисел в системе команд микропроцессора существует специальная команда:

ААА ; ASCII-коррекция после сложения для представления в символьном виде.

Команда работает с регистром AL и анализирует значение его младшей тетрады. Если это значение меньше 9, то флаг cf сбрасывается в 0, и осуществляется переход к следующей команде. Если это значение больше 9, то выполняются следующие действия:

  1. к содержимому младшей тетрады al прибавляется 6, тем самым значение десятичного результата корректируется в правильную сторону;

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

Например при суммировании 06+07 в al находится 0000 1101. После команды ааа в регистре будет 1101+0110=0011, то есть двоичное 0000 00112 =310, а флаг cf установится в 1, то есть запомнится перенос в старший разряд. Далее необходимо использовать команду сложения adc, которая учтёт перенос из предыдущего разряда.

Пример: сложим два неупакованных BCD-числа 71+54

Строки 14 и 15 складывают цифры в очередных разрядах BCD-чисел, при этом учитывается возможный перенос из младшего разряда. Команда ааа корректирует результат сложения, формируя в аl BCD-число и, при необходимости, устанавливая в 1 флаг cf. Строка 20 учитывает возможность переноса при сложении цифр из самых старших разрядов чисел. Результат сложения формируется в поле sum.

Вычитание неупакованных BCD-чисел

Пример1.

6=000 0110

-

3=0000 0011

=

3=0000 0011 – результат верный

Пример2.

6=0000 0110

-

7=0000 0111

=

-1=1111 1111 – результат не правльный. Правильный результат должен быть 0000 10012 =910 . При этом предполагается заём из старшего разряда, то есть в случае с BCD-числами должно быть выполнено вычитание 16-7. Таким образом, видно, что результат вычитания нужно корректировать. Для этого существует специальная команда:

AAS – коррекция результата вычитания для представления в символьном виде.

Команда работает с регистром al, анализируя его младшую тетраду: если её значение меньше 9, то флаг cf сбрасывается в 0, и управление передаётся следующей команде. Если значение тетрады в al больше 9, то команда aas выполняет следующие действия:

  1. из содержимого младшей тетрады регистра al вычитается 6;

  2. обнуляет старшую тетраду регистра al;

  3. устанавливает флаг cf в 1, фиксируя воображаемый заём из старшего разряда.

Команда aas применяется вместе с основными командами вычитания sub и sbb.

Например: 1-4

.data

a db 01h

b db 04h

.code

xor ax,ax ; очистка ах

xor bx,bx ; очистка bx

mov al,a ; в al занесли 1

sbb al,b ; al=1-4 ; 0000 0001-0000 0100=1111 1101

aas ; 1101-0110=0111, al=0000 0111

Умножение

Для того, чтобы перемножить два одноразрядных BCD –числа необходимо:

  1. поместить один из сомножителей в регистр al (этого требует команда mul);

  2. поместить второй операнд в регистр или память, отведя байт;

  3. перемножить сомножители командой mul (результат будет в ах);

  4. результат получится в двоичном коде и его нужно корректировать.

Для коррекции результата после умножения применяется команда:

AAM – коррекция результата умножения для представления в символьном виде.

Команда не имеет операндов и работает с регистром ах следующим образом:

  1. делит al на 10;

  2. результат деления записывается так: частное – в al, остаток в ah.

В результате после выполнения команды aam в регистрах al и ah находятся правильные двоично-десятичные цифры произведения двух цифр.

Деление

Предварительно в регистре ах необходимо получить две неупакованные BCD –цифры делимого. Далее используется команда:

AAD – коррекция деления для представления в символьном виде.

Команда преобразует двузначное неупакованное BCD-число в регистре ах в двоичное число. Это двоичное число далее будет использоваться в качестве делимого в операции деления. Кроме преобразования команда aad помещает полученное двоичное число в регистр al. Делимое будет двоичным числом из диапазона 0…99. Алгоритм, по которому команда aad осуществляет его преобразование, состоит в следующем:

  1. умножается старшая цифра исходного BCD –числа в ах (содержимое ah) на 10;

  2. выполняется сложение ah+ al, результат которого (двоичное число) заносится в al;

  3. обнуляется содержимое ah.

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

Команду aad можно также использовать для перевода неупакованных BCD-чисел из диапазона 0…99 в их двоичный эквивалент.

Например, разделим десятичное 25 на 5:

mov ax,0205h ; 25 в неупакованном формате

mov bl,5

aad ; теперь в АХ находится 19h

div bl ; АХ = 0005

Флаги SF, ZF и PF устанавливаются в соответствии с результатом, OF, AF и CF не определены.

Ah=0000 0010; al=0000 0101

1) 0000 0010 *0000 1010=0001 0100=ah

2) 0001 1010+0000 0101=0001 1001=al

3) ah=0000 0000 ; ax=0000 0000 0001 10012=2510

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

Сложение

Пример1:

67=0110 0111

+

75=0111 0101

=

142=1101 1100=22010 – это неверный результат. Правильный результат 0001 0100 0010 (14210)

Для корректировки результата микропроцессор предоставляет команду:

DAA – коррекция результата сложения для представления в десятичном виде.

Команда daa преобразует содержимое регистра al в две упакованные десятичные цифры по следующему алгоритму:

1) если младшие четыре бита AL больше либо равно 9 или флаг AF = 1, то AL увеличивается на 6, CF устанавливается, если при этом сложении произошел перенос, и AF устанавливается в 1.

2) иначе AF = 0.

3) если теперь старшие четыре бита AL больше 9 или флаг CF = 1, то AL увеличивается на 60h и CF устанавливается в 1.

4) иначе CF = 0.

Флаги AF и CF устанавливаются, если в ходе коррекции происходил перенос из первой или второй цифры соответственно, SF, ZF и PF устанавливаются в соответствии с результатом, флаг OF не определен.

Например, если AL содержит число 19h, последовательность команд:

inc al

daa

приведет к тому, что в AL окажется 20h (а не 1Ah, как было бы после INC).

1) 19h+1=1A

2) 1A16 =1 10102 +01102 =10 00002 =2016

Вычитание

Пример1: 67-75

67=0110 0111

+

-75=1011 0101 (75=1001011, 0110100+1=10110101=-75)

=

-8=0001 1100=28 – результат неверный. Верный результат 0000 1000=810. Контроль за знаком осуществляется с помощью флага cf, который фиксирует заём из старших разрядов.

Вычитание BCD-чисел осуществляется командами sub ил sbb. Коррекция результата осуществляется командой:

DAS – коррекция результата вычитания для представления в десятичном виде.

Команда работает по следующему алгоритму:

1) если младшие четыре бита AL больше 9 или флаг AF = 1, то AL уменьшается на 6, CF устанавливается, если при этом вычитании произошел заем, и AF устанавливается в 1.

2) иначе AF = 0.

3) если теперь старшие четыре бита AL больше 9 или флаг CF = 1, то AL уменьшается на 60h и CF устанавливается в 1.

4) иначе CF = 0.

Например, если AL содержит число 20h, последовательность команд

dec al

das

приведет к тому, что в AL окажется 19h (а не 1Fh, как было бы после DEC).