Программирование на языке Ассемблер. Учебное пособие
.pdfADD – прибавить второй операнд к первому.
SUB – вычесть второй операнд из первого.
CMP – вычесть второй операнд из первого только для флагов.
AND – логическое И между всеми битами двух операндов. При этом соблюдаются правила:
1 AND 1 = 1
1 AND 0 = 0
0 AND 1 = 0
0 AND 0 = 0
TEST – то же самое, что AND, но только для флагов.
OR – логическое ИЛИ между всеми битами двух операндов. При этом соблюдаются правила:
1 OR 1 = 1
1 OR 0 = 1
0 OR 1 = 1
0 OR 0 = 0
XOR – логическое XOR (исключающее ИЛИ) между всеми битами двух операндов. При этом соблюдаются правила:
1 XOR 1 = 0
1 XOR 0 = 1
0 XOR 1 = 1
0 XOR 0 = 0
Вторая группа: MUL, IMUL, DIV, IDIV
Эти типы операндов поддерживаются:
REG memory
REG: AX, BX, CX, DX, AH, AL, BL, BH, CH, CL, DH, DL, DI, SI, BP, SP.
memory: [BX], [BX+SI+7], переменная, и т. п.
Команды MUL и IMUL влияют только на флаги: CF, OF. Если результат превышает размер операнда, то эти фла-
ги установлены в 1, если результат умещается в размер операнда, то эти флаги установлены в 0.
31
Для команд DIV и IDIV флаги не определены. MUL – беззнаковое умножение:
если операнд – это байт:
AX = AL операнд;
если операнд – это слово:
(DX AX) = AX операнд.
IMUL – умножение со знаком: если операнд – это байт:
AX = AL операнд;
если операнд – это слово:
(DX AX) = AX операнд.
DIV – беззнаковое деление: если операнд – это байт:
AL = AX / операнд
AH = остаток (модуль);
если операнд – это слово:
AX = (DX AX) / операнд
DX = остаток (модуль). IDIV – деление со знаком:
если операнд – это байт:
AL = AX / операнд
AH = остаток (модуль);
если операнд – это слово:
AX = (DX AX) / операнд
DX = остаток (модуль).
Третья группа: INC, DEC, NOT, NEG
Эти типы операндов поддерживаются:
REG memory
REG: AX, BX, CX, DX, AH, AL, BL, BH, CH, CL, DH, DL, DI, SI, BP, SP.
memory: [BX], [BX+SI+7], переменная, и т. п.
32
Команды INC и DEC влияют только на флаги: ZF, SF, OF,
PF, AF.
Команда NOT не влияет ни на какие флаги!
Команда NEG влияет только на следующие флаги: CF, ZF,
SF, OF, PF, AF.
NOT – инвертирование каждого бита операнда.
NEG – меняет знак операнда (дополнение до двух). Обычно она инвертирует каждый бит операнда, а затем прибавляет к нему единицу. Например, 5 преобразуется в -5, а -2 преобразуется в 2.
33
7. УПРАВЛЕНИЕ ХОДОМ ПРОГРАММЫ
Управление ходом программы – очень важная вещь. Это то, что заставляет программу принимать решения в зависимости от поставленных условий.
7.1. Безусловные переходы
Основная команда, которая передает управление в другую точку программы, это JMP.
Основной синтаксис команды JMP:
JMP метка
Чтобы объявить метку, в программе необходимо написать её имя и в конце добавить двоеточие «:». Метка может быть любой комбинацией символов, но не должна начинаться с цифры. Например, ниже представлены три правильных объявления меток:
label1:
label2:
a:
Метка может быть объявлена на отдельной строке или перед любой другой командой, например:
x1:
MOV AX, 1 x2: MOV AX, 2
Ниже представлен пример программы с использованием команды JMP:
ORG 100h |
|
MOV AX, 5 |
; записать в AX число 5 |
MOV BX, 2 |
; записать в ВX число 5 |
JMP calc |
; перейти к 'calc' |
back: JMP stop |
; перейти к 'stop' |
calc: |
|
ADD AX, BX |
; прибавить BX к AX |
34
JMP back |
; перейти к 'back' |
stop: |
|
RET |
; вернуться в операционную систему |
END |
; директива для прекращения компиляции |
Конечно, есть более простой путь для вычисления результата с двумя числами, но это хороший пример применения команды JMP.
Как можно видеть из этого примера, команда JMP может передавать управление другому участку программы, который может находиться как после этой команды, так и перед ней. Этот переход может быть осуществлен в пределах текущего сегмента кода (65535 байтов).
7.2. Короткие условные переходы
Подобно команде JMP, которая выполняет безусловный переход, существуют команды, которые осуществляют условный переход (переход, который осуществляется только в том случае, если выполняется определенное условие). Эти команды разделяются на три группы. Первая группа только проверяет отдельный флаг, вторая – сравнивает числа со знаком, третья – сравнивает числа без знака.
Команды перехода, проверяющие одиночный флаг, представлены в табл. 2.
|
|
|
|
|
Таблица 2 |
|
|
|
|
|
|
Команда |
|
Описание |
|
Условие |
Обратная |
|
|
команда |
|||
|
Переход, |
если |
«равно» |
|
|
JZ,JE |
(«ноль»). Т. е. если сравни- |
ZF = 1 |
JNZ,JNE |
||
ваемые значения равны, то ZF |
|||||
|
= 1 и переход выполняется |
|
|
||
JC,JB,JN |
Переход, если есть перенос («ни- |
CF = 1 |
JNC,JNB, |
||
AE |
же»,«невышеилиравно») |
JAE |
|||
35
Окончание табл. 2
Команда |
Описание |
Условие |
Обратная |
|
|
|
|
команда |
|
JS |
Переход по знаку |
SF = 1 |
JNS |
|
JO |
Переход по переполнению |
OF = 1 |
JNO |
|
JPE, JP |
Переход, если есть паритет |
PF = 1 |
JPO |
|
или паритет четный |
||||
|
|
|
||
JNC, JNB, |
Переход, если нет переноса |
CF = 0 |
JC, JB, |
|
(«выше или равно» или «не |
||||
JAE |
ниже») |
|
JNAE |
|
|
|
|
||
JNS |
Переход, если нет знака |
SF = 0 |
JS |
|
JNO |
Переход, если нетпереполнения |
OF = 0 |
JO |
|
JPO,JNP |
Переход, если нет паритета |
PF = 0 |
JPE, JP |
|
|
или паритет нечетный |
|
|
Как видно из таблицы, существуют команды, которые выполняют одинаковые действия. Это нормально, они даже ассемблируются в одинаковый машинный код, поэтому необходимо запомнить, что при компиляции команды JE, после дизассемблирования будет получена команда JZ.
Различные имена используются для того, чтобы делать программы более легкими для понимания и кодирования.
Команды перехода для чисел со знаками представлены в табл. 3.
|
|
|
Таблица 3 |
|
|
|
|
|
|
Команда |
Описание |
Условие |
Обратная |
|
команда |
||||
JE, JZ |
Переход,если«равно»(=). |
ZF = 1 |
JNE, JNZ |
|
Переход, если«ноль». |
||||
|
|
|
||
JNE, JNZ |
Переход,если«неравно»(<>). |
ZF = 0 |
JE, JZ |
|
|
Переход,если«неноль». |
|
|
|
|
Переход,если«больше»(>). |
ZF = 0 |
|
|
JG, JNLE |
Переход, если «не меньше или |
and |
JNG, JLE |
|
|
равно»(not<=). |
SF = OF |
|
36
Окончание табл. 3
Команда |
Описание |
Условие |
Обратная |
|
|
|
команда |
JL, JNGE |
Переход,если«меньше»(<). |
SF <> OF |
JNL, JGE |
Переход, если «не больше или |
|||
|
равно»(not >=). |
|
|
|
Переход, если «больше или |
|
|
JGE, JNL |
равно» (>=). |
SF = OF |
JNGE, JL |
Переход, если «не меньше» |
|||
|
(not <). |
|
|
|
Переход, если «меньше или |
ZF = 1 |
|
|
равно» (<=). |
|
|
JLE, JNG |
or |
JNLE, JG |
|
|
Переход, если «не больше» |
SF <> OF |
|
|
(not >). |
|
|
В табл. 3 знак <> – означает «не равно».
Команды перехода для чисел без знаков представлены в табл. 4.
|
|
|
Таблица 4 |
|
|
|
|
|
|
Команда |
Описание |
Условие |
Обратная |
|
команда |
||||
JE, JZ |
Переход, если «равно» (=). |
ZF = 1 |
JNE, JNZ |
|
Переход, если «ноль». |
||||
|
|
|
||
|
Переход, если «не равно» |
|
|
|
JNE, JNZ |
(<>). |
ZF = 0 |
JE, JZ |
|
|
Переход, если «не ноль». |
|
|
|
JA, JNBE |
Переход, если «выше» (>). |
CF = 0 |
JNA, JBE |
|
Переход, если «не ниже или |
and |
|||
|
равно» (not <=). |
ZF = 0 |
|
|
JB, JNAE, |
Переход, если «ниже» (<). |
|
JNB, JAE, |
|
Переход, если «не выше или |
CF = 1 |
|||
JC |
равно» (not >=). |
JNC |
||
|
||||
|
Переход по переносу. |
|
|
37
Окончание табл. 4
Команда |
|
Описание |
Условие |
Обратная |
||
|
|
|
|
|
команда |
|
|
Переход, если «выше или |
|
|
|||
JAE, JNB, |
равно» (>=). |
|
CF = 0 |
JNAE, JB |
||
JNC |
Переход,если«нениже»(not <). |
|||||
|
|
|||||
|
Переход, если«нетпереноса». |
|
|
|||
|
Переход, |
если |
«ниже или |
CF = 1 |
|
|
JBE, JNA |
равно» (<=). |
|
JNBE, JA |
|||
|
or |
|||||
Переход, |
если |
«не выше» |
||||
|
(not >). |
|
|
ZF = 1 |
|
|
|
|
|
|
|
||
Обычно, если требуется сравнить два числовых значения, то используют команду CMP, она делает то же самое, что и команда SUB (вычитание), но не сохраняет результат, а влияет только на флаги.
Логика очень простая. Например, требуется сравнить числа 5 и 2:
5 – 2 = 3
результат – НЕ НОЛЬ (Флаг Нуля – Zero Flag (ZF) установлен в 0).
Другой пример, требуется сравнить 7 и 7:
7 – 7 = 0
результат – НОЛЬ! (Флаг Нуля – Zero Flag (ZF) установлен в 1 и команды JZ или JE выполнят переход).
Нижеприведенпримеркоманды CMP и условногоперехода: include emu8086.inc
ORG 100h
MOV AL, 25 ; записать в AL число 25
MOV BL, 10 ; записать в BL число 10 CMP AL, BL ; сравнить AL с BL.
JE equal |
; если AL = BL (ZF = 1), то перейти |
|
; к метке equal |
PUTC 'N' |
; иначе, если AL <> BL, то продолжить |
|
;выполнение |
38
JMP stop |
; программы |
– напечатать 'N' и |
|
|
; перейти к |
метке stop |
|
equal: |
; если программа на этой метке, |
||
PUTC 'Y' |
; то AL |
= BL, поэтому выводим на |
|
|
; экран |
'Y' |
|
stop:
RET ; сюда приходим в любом случае
END
Попробуйте вышеописанный пример с различными числами в AL и BL, откройте флаги, щелкнув по кнопке [FLAGS]. Используйте [Single Step] – пошаговый режим и наблюдайте за происходящим. Не забывайте перекомпилировать и перезагружать вашу программу после каждого сделанного вами в ней изменения (используйте клавишу F5).
Все условные переходы имеют одно серьезное ограничение – в отличие от команды JMP, они могут выполнять переход только на 127 байтов вперед или на 128 байтов назад (необходимо учитывать, что большие команды ассемблируются в 3 и более байтов).
Но это ограничение можно легко преодолеть, используя следующий метод:
–взять обратную команду из табл. 4 и выполнить переход
кметке label_x;
–использовать команду JMP для перехода к нужному участку программы;
–определить метку label_x: только после команды JMP (label_x: – может быть любым именем).
Пример:
include emu8086.inc ORG 100h
MOV AL, 25 |
; |
записать в |
AL |
число 25 |
|
MOV BL, |
10 |
; |
записать в |
BL |
число 10 |
CMP AL, |
BL ; |
сравнить AL с |
BL |
||
JNE not_equal |
; переход, если AL <> BL (ZF = 0) |
||||
39
JMP equal not_equal:
;представим, что здесь у нас
;размещается код, который ассемблируется
;более чем в 127 байтов...
PUTC 'N' |
; если мы оказались здесь, то AL <> BL, |
JMP stop |
; тогда печатаем 'N', и |
|
; переходим к метке stop |
equal: |
; если мы оказались здесь, |
PUTC 'Y' |
; то AL = BL, тогда печатаем 'Y' |
stop: |
|
RET |
; сюда приходим в любом случае |
END |
|
Другой, реже используемый метод, представляет собой применение непосредственного значения (адреса) вместо метки. Если непосредственное значение начинается с символа '$', то выполняется относительный переход – переход относительно текущего адреса. Компилятор вычисляет команду, которая находится по заданному смещению, и выполняет переход непосредственно к ней. Например:
ORG 100h
;безусловный переход вперед
;пропускаем следующие два байта
JMP $2
a |
DB |
3 |
; |
1 |
байт |
b |
DB |
4 |
; |
1 |
байт |
;переход назад на 7 байтов, если BL <> 0
;(команда JMP занимает 2 байта)
MOV BL,9
DEC BL ; 2 байта
CMP BL, 0 ; 3 байта
JNE $-7
RET
END
40
