Назад
+<CF> от предыдущего сложения
9
Итого: 95.
Рассмотрим пример сложения многобайтовых чисел.
Пример 1 Пусть 1-ое слагаемое находится в
M
1501
,M
1500
(двухбайтовое число), а второе слагаемое в BX. Сумму
поместить в M
1601
,M
1600.
Ответ: Если складывать по байтам, то это будет выглядеть так:
ADD BL,[1500]; Сложить младшие байты чисел
MOV [1600],BL; и переслать младшую часть суммы в M
1600
.
ADC BH,[1501]; Сложить старшие байты слагаемых и + возмож-
MOV [1601],BH; ный перенос от предыдущей суммы и M
1601.
6.3 Операция вычитания
Эта команда выполняется чуть сложнее, чем команда сложения.
В отличие от сложения, операция вычитания простыми логическими
средствами не реализуется. Операция вычитания производится
следующим образом:
а) вычитаемое преобразуется в дополнительный код;
б) дополнительный код вычитаемого складывается с
уменьшаемым.
Само собой разумеется, что все это выполняется автоматически.
В отличие от сложения, перенос в этом случае направляется в CF через
инвертор. Посмотрим на абстрактных примерах, что из этого выходит.
Пусть A —уменьшаемое, B —вычитаемое.
Случай 1: A>B. Пусть A=1000, B=0101 (двоичные числа).
Преобразуем B в дополнительный код (без знака):
0101
инверсия
1010
+ 0001
B
доп
=1011дополнительный код B.
Теперь складываем: A+B
доп
:
1000
+1011
1 0011
Перенос = 1. Но этот перенос при вычитании инвертируется, и только
потом попадает в CF. Значит, в этом случае CF=0. Еще раз: 1000
2
-
0101
2
= 0011
2
и CF=0. (Кстати, результат получили в прямом коде).
Случай 2: A<B. Пусть A=0101, а B=1000. По той же технологии
преобразуем B:
41
1000
инверсия
0111
+0001
B
доп
=1000дополнительный код B.
Складываем: A+B
доп
:
0101
+1000
0 1101
Перенос=0
Переполнения нет, но флажок CF=1, так как он устанавливается
переполнением через инвертор.
А теперь подставим значение CF=1 в качестве старшего бита
уменьшаемого и вычтем вычитаемое B:
<CF>
1 0101
— 1000
1101
Получили тот же результат, что и выше. Вывод: при вычитании
флаг CF имеет смысл заема! Но заем предполагает наличие старшей
части числа. А если ее нет? Тогда необходимо вспомнить, что все числа
в АЛУ представлены в дополнительном коде, и если после вычитания
CF=1 , а занимать не у «кого», то полученный результат есть
отрицательное число в дополнительном коде. Чтобы получить результат
в истинном виде, необходимо полученное отрицательное число
(беззнаковое) перевести в прямой код:
CF=1 1101
инверсия
CF=1 0010
+0001 инкремент, чтобы «не трогать» <CF>
CF=1 0011
Получили: 5
10
—8
10
= -3
10
, где <CF> имеет смысл знака.
Ассемблер команды вычитания (пример):
SUB AX,BX ; AX:=<AX>-<BX>; заем (если есть) — в CF.
6.4 Команда вычитания с учетом заема
Эта команда вычитания как раз и учитывает возможный заем от
предыдущего вычитания. Рассмотрим следующий пример.
Пример 2 — Пусть в ячейках памяти M
1001
и M
1000
находится
уменьшаемое, а в DX-вычитаемое. Разность отправить в DX.
Ответ: Составляем программу с демонстрацией использования
процедуры заема на ассемблере:
SUB [1000],DL ; M
1000
:=<M
1000
>-<DL>, заем (если есть) — в CF,
MOV DL,[1000]
; и младшую часть разности отправить в DL.
SBB [1001],DH ; M
1001
:=<M
1001
>-<DН>-заем (если он есть),
42
MOV DH,[1000] ; и разность в DH.
6.5 Команда логической функции ИЛИ
Основные логические функции булевой алгебрыэто И, ИЛИ,
НЕ. В таблице представлены только две из них: И (AND) и ИЛИ (OR).
Дело в том, что операция НЕ оперирует только с одним адресатом,
поэтому ее здесь нет. Ее рассмотрим в конце этой главы. А пока
вернемся к команде ИЛИ (дизъюнкция).
Рассмотрим следующий пример. Пусть в AL=3C (00111100
2
), в
AH=41 (01000001
2
). Что получится после выполнения OR AH,AL?
Учитывая, что с многобитовыми событиями логические операции
выполняются по-битно, можем записать:
00111100 (3С)
(ИЛИ)
01000001 (41)
01111101
AH = 7 D.
Эта логическая операция используется достаточно часто.
Например, еобходимо установить бит D5 ячейки памяти M
1000
в единицу,
оставив неизменными остальные биты M
1000
. Это делается очень просто.
Составим байт, у которого бит D
5
=1: 00100000
2
=20
16
. Выполним
команду:
OR BYTE [1000],20
И все.
6.6 Команда логической функции И
Операция И (конъюнкция) выполняется аналогично ИЛИ
(смотрите п.5 таблицы 5).
Рассмотрим пример. Пусть в регистре АХ хранятся следующие
события: AH=42, AL=7C. Что будет в регистре AL после выполнения
команды AND AL,AH?
01111100 (7С)
И)
01000010
(42)
01000000
AL= 4 0
Эта команда тоже достаточно популярна. Например, она позволяет
сбрасывать в «0»один бит в байте (слове), оставляя неизменными
другие биты. Пусть, например, необходимо установить бит D5 ячейки
памяти M
1000
в «0», оставив при этом неизменными остальные биты.
Составляем байт, у которого D5=0: 1101111
2
=DF. Выполним команду:
AND BYTE [1000],DF.
Бит D5 ячейки памяти M
1000
будет сброшен в «0».
43
6.7 Команда «Исключающая ИЛИ»
Это команда сложения по модулю 2. При сложении чисел по
модулю N в формировании разрядной суммы игнорируются все
переносы, которые получаются при превышении разрядной суммой
числа N.
Составим таблицу истинности сложения по модулю 2 для 2-х двоичных
переменных A и B:
Таблица 6 —Таблица истинности mod2
А
В
АВ
Комментарий
0
0
0
Внутриразрядная сумма = 0
0
1
1
Внутриразрядная сумма =
1
1
0
1
Внутриразрядная сумма =
1
1
1
0
Внутриразрядная сумма =
0,
Переполнение
игнорируется
А теперь выпишем ДНФ (дизъюнктивную нормальную форму
записи) этой логической функции: AB=((-A)B)(A(-B)). Что же
является важным из таблицы истинности функции AB?
Произведем анализ зависимости выходной функции AB от
переменной B, если A=1. Он позволяет сделать вывод, что при
A=1 существует зависимость A+B = -В, то есть B инвертируется.
А верхние две строчки таблицы 6 говорят об обратном: если A=0,
то B не инвертируется.
Вот эти свойства и используются, главным образом. Обратимся к
примеру. Пусть необходимо изменить бит D5 ячейки памяти M
1000
на
противоположное состояние. Составляем слово, где бит D5=1 (событие
A), остальные биты равны нулю: 00100000
2
=20
16
. После выполнения
команды:
XOR BYTE [1000],20
состояние разряда D5 ячейки памяти M
1000
изменится на
противоположное.
Большой популярностью пользуется такое применение команд
сложения по модулю 2:
XOR AX,AX ; обнулить AX;
XOR SI,SI ; обнулить SI;
Эти команды сбрасывают в «0» регистры AX и SI, что собственно и
должно быть при сложении по модулю 2 любого числа с самим собой.
44
6.8 Команда сравнениякомпарации
Здесь трудно что-либо добавить к тем комментариям, которые есть
в таблице 5. Сравнение производится на основе операции вычитания,
но разность нигде не фиксируется, потому что нужна не она, а ее
признаки: был ли заем? нулевой ли результат? и тому подобное
которые заносятся во флажковые биты.
Ассемблер команды (пример):
CMP AX,[1CFO].
И в заключение главыкоманда инверсии:
а) Ассемблер: NOT dst. Комментарий: dst:=-<dst>.
б) Машинный код операции:
Необходимо отметить, что эта команда флажки «не трогает». Так
реализовано преднамеренно, так как часто бывает необходимо
проинвертировать что-то, при этом состояние флажков сохранить.
Естественно, что бывает и наоборот, когда нужно проинвертировать и
одновременно в соответствии с проинвертированным результатом
взвести флажки. Для этих целей используют команду XOR dst, FF, если
нужно инвертировать байт, или XOR dst, FFFF, если слово. Суть
действия этой команды изложена в примерах в п. 7 для команды XOR.
7 Команды ветвления и переходов
7.1 Команды ветвления
Это важнейшая группа команд, позволяющая принимать решение.
Все команды, которые до сих пор рассматривались, выполняли действие.
А вот возможностью принять решение по результатам каких-либо
действий обладают только команды ветвлений. Ветвлениеэто переход
(скачок) на указанное в команде перехода место, если выполнилось
указанное в команде условие.
К примеру, команда «jump no Z 5CЗ в переводе на русский язык
означает: прыгнуть по адресу 5CЗF, если нет Z (ассемблер: JNZ 5СЗF).
Она осуществляет прыжок на адрес 5СЗF, то есть следующая команда
будет выбираться по адресу 5СЗF, если флаг Z = 0. В противном случае
45
происходит выборка следующей команды (JNZ —пустая команда, если Z
= 1).
Таких команд МП 8086 реализует в количестве 16 шт. Но все эти
команды имеют единый формат КОПа и команды в целом.
Формат КОПа:
Из формата команды следует, что типов команд —2
3
= 8. Каждая
команда имеет свой антипод. То есть, если есть команда JNZ Addr
(перейти, если Z= 0), то и есть ее антипод JZ Addr (перейти, если Z= 1).
Команда условного перехода в ассемблере всегда начинается с
буквы J (jump — скачок). Поэтому в приведенной ниже таблице
7 указываются недостающие буквы, следующие за J для различных
типов команд, и соответствующие им условия переходов.
В скобках указаны допустимые альтернативные обозначения.
То есть можно записать JB, а можно и JNAE.
Таблица 7 — Синтез команд ветвлений по условиям ветвлений
Тип команды Признак D0 = 0 Признак D0 =0
Ассемблер(доп.
J)
Условие перехода
А
ссемблер (доп. к
J)
Условие
перехода
000 O OF = 1
NO
OF = 0
001 B (C,NAE) CF = 1 NB(NC,AE) CF = 0
010 E(Z) ZF = 1 NE(NZ) ZF = 0
011 BE(NA) CF V ZF = 1 NBE (A) CF V ZF=0
100 S SF = 1 NS SF = 0
101 P (PE) PF = 1 NP (P0) PF = 0
110 L (NGE)
SF OF = 1
NL (GE)
SF OF = 0
111 LE (NG)
(SF OF) V
ZF=1
NLE (G)
(SF OF) V
ZF=0
Для системных специалистов определяющим является не
обозначение команды в ассемблере, а условие перехода, которое
определяется состоянием или состояниями тех или иных флажков.
То есть, по какому событию происходит переход. Если нужно перейти по
условию OF = 1, то пишем JO; если переход осуществляется по
условию CF =1, то пишем JC, хотя C (вторая строчка в таблице 7) —
альтернативное обозначение, о котором программисты часто не знают.
Дело в том, что для программистов первоначальным является само
условие перехода: «больше», «меньше» и тому подобное.
Из таблицы 7 следует, что условия переходов диктуют 5 флагов
(смотрите главу 2):
46
OF — арифметическое переполнение. Флаг OF=1, если
относительно знакового бита в результате выполнения, например,
арифметических команд произошла следующая метаморфоза:
(+)+(+)=(-); (+)-(-)=(-);
(-)+(-)=(+); (-)-(+)=(+);
(здесь в скобках указаны знаки).
CF —переполнение (байта, слова) или заем, если было вычитание.
ZF —признак нулевого результата. ZF = 1 — значит получили в
результате команды АЛУ, INC, или DEC — результат, равный нулю.
SF —знак. Здесь все просто: если команда воздействует на флаги, то флаг
SF запоминает содержимое старшего бита данных:
а) SF:= <D7>, если операнд — 1 байт;
б) SF:= <D15>, если операнд — 1 слово.
PF — событие (признак) наличия в результате четного (PF = 1)
числа единиц; касается только байта (если слово, томладшего байта).
Приведем пример. Пусть в AX хранится: 1DCO. Выполним команду:
AND AX,1245
Н-код Двоичный код
1DCO 0001 1101 1100 0000 (АХ)
И И
1245
0001 0010 0100 0101 data
Результат=1040 0001 0000 0100 0000 (АХ)
(D15)
Каково же будет состояние флагов?
OF = 0; арифметического переполнения не было (хотя для этой
команды это событие бессмысленно, но ЦА выполняет свой алгоритм);
CF = 0; логическая операцияпоразрядная операция, и у нее не
может быть переноса (заема);
ZF = 0; результат отличен от нуля: 1040 0;
SF = 0; знаковый флажок запоминает: SF= < D15> = 0; хотя это для
данной команды то же не имеет смысла, но ЦА выполняет свой алгоритм;
PF = 0 в младшем байте одна единица, то есть нет четного
числа единиц (кстати, 0 —это четное число единиц).
Знание правил установки флагов и позволяет эффективно
использовать команды условного перехода.
В таблице 7 указаны и комбинированные условия переходов.
В этом случае лучше использовать для обозначения команды в
ассемблере буквы, не входящие в скобки. Разберем эти условия
подробнее.
1) CF V ZF. Допустим, из числа А вычитается число В, или
сравнивается число А с числом В (наиболее часто используемый прием
применения этого условия). Каковы варианты состояний флагов,
соответствующие этой функции? Для ответа на этот вопрос составим
47
таблицу истинности функции CF V ZF (таблица 8), исходя из смысла
указанных операций. Исходя из таблицы 8, можно сделать следующие
заключения:
Таблица 8
CF ZF CFVZF Событие
0 0 0 A > B
0 1 1 A = B
1 0 1 A < B
1 1 Не может быть
а) если CFvZF=1, то операнды или равны при ZF =1 (Е
eguivalent — равно), или при CF =1 (заем) операнд А меньше
операнда В ( В — below —меньше, ниже). Следовательно, команда
перехода означает: JBE — перейти, если меньше или равно (то
есть, не больше);
б) если CFvZF= 0, то все наоборот, и команда перехода:
JNBE — перейти, если больше (то есть нет событий В и Е: NO
B&E).
2) SFOF. Это условие рассчитано на результат операций со
знаковыми числами. Не вдаваясь в подробности анализа, можно
отметить, что если после вычитания SFOF = 1, то вычитаемое со
знаком меньше уменьшаемого со знаком (L — less — меньше).
Соответственно команды переходов:
а) JL — перейти, если меньше (для результата со знаком);
б) JNL – перейти, если не меньше (для результата со
знаком). 3) (SFOF)vZF. Это условие рассчитано также при
операциях над
числами со знаком. Например, если после вычитания: (SFOF)vZF= 1,
то полученный результат со знаком меньше или равен нулю, то есть не
больше (LE —меньше или равно). Соответственно и команда перехода
по этому условию: JLE.
Команда перехода по обратному условию (SF+OF)vZF=0: JNLE.
Из всех команд ветвлений наиболее «заезженные» команды: JZ,
JNZ, JC, JNC, JBE, JNBE.
После того, как мы рассмотрели условия переходов и ассемблер
команд условных переходов, можно перейти к методике формирования
адреса перехода.
1) Адрес перехода короткий. Его максимальная величина:
±127
10
адресов. Такой размер продиктован практической
целесообразностью. Действительно, при составлении программ условные
переходы используются, как правило, в пределах «видимости», то есть в
очень маленьких пределах по сравнению с общим размером программ.
48
2) Поскольку переход бывает необходимым как вперед, так и назад,
то он, как и смещение disp (смотрите главу 3), задается в машинном коде
командыв дополнительном коде со знаком как смещение относительно
текущего адреса, находящегося в IР.
3) Для удобства написания программы в ассемблере (для чего
собственно и придуман ассемблер) в адресной части команды условного
перехода ставится не смещение, а непосредственный адрес перехода в
текущем сегменте программы, а смещение вычисляется транслятором.
Теперь все это проследим на отдельных примерах синтеза команд в
ассемблере и машинных кодах.
Пример 1 По адресу 0100 расположить команду условного
перехода с условием: если результат 0 (Z = 1), то перейти на 010F.
Ответ:
а) в ассемблере (все просто):
JZ 010F
б) в машинном коде:
сначала составляем КОП :
1) тип команды JZ = 010 (третья строчка таблицы 7);
2) признак = 0 (таблица 7).
Теперь составляем второй байт командысмещение (disp).
Учитывая, что размер формата команды JZ Addr — 2 байта, текущее
значение адреса команды будет следующим: IP = 0100 + 2 = 0102.
Вычитаем из конечного текущий адрес:
disp = 010F — 0102 = 000D = 13
10
< 127
10
.
Окончательно:
Address Command Assembler
0100 74 JZ 010F
0101 OD
Пример 2 По адресу 0100 расположить команду условного
перехода с условием: если больше (без знака), то перейти на адрес 0092:
Ответ:
а) в ассемблере:
JNBE 0092
б) в машинном коде:
1) КОП:
49
2) Смещение:
Смещение (disp) вычисляем относительно текущего значения
адреса (содержимого IP после выборки команды), учитывая что команда
JNBE (JA) — двухбайтовая. Текущее значение адреса: IP= 0100 + 2 =
0102. Ищем разность между конечным и текущим адресом: (0092 —
0102) = -0070. Берем только младший байт -70 и преобразуем это
отрицательное число из прямого кода в дополнительный (D7 —знак):
Прямой Обратный Дополнительный
S S S (S —знаковый бит)
1.1110000
2
1.0001111
2
1.0010000
2
= 90
16
.
Окончательно:
Address Command Assembler
0100 77 JNBE 0092
0101 90
Пример 3 – тоже самое, что и в примере 1, только перейти на
адрес 182.
Ответ:
Вычислим смещение: 182 —102 = 80
16
= 128
10
> 127
10
-
неосуществимо! Перебор на 1 адрес, так как максимальное смещение в
этом случае может быть только 127
10
= 7F
16
.
7.2 Команды перехода
Кроме команд типа JUMP с условным переходом (ветвления) есть
и команды с безусловным переходом, которые позволяют «прыгать» не
только в пределах ±7F, но и даже между сегментами. Рассмотрим их.
JMP — команда с безусловным переходом (пять типов)
1) Короткая, с относительной адресацией (смещением), 2-х
байтовая; максимальная величина disp = ±7F.
а) ассемблер: JMP SHORT Addr;
б) КОП = ЕВ.
Пример 4 Команда расположена на 0100 адресе. Прыгнуть на
адрес 0105.
Определяем текущее значение IP: IP = 0100 + 2 = 0102.
Смещение: 0105 —0102 = 0003 = 03 (1 байт).
50