Эта статья начинает цикл, посвященный более глубокому изучению ассемблера. Теперь каждая статья будет разделена на две части. В первой части будут рассматриваться некоторые теоретические свнедения, а во второй - новые команды, примеры кода и приемы программирования на ассемблере.
Формат машинных команд IA-32
В настоящее время существует несколько сотен (если не больше) языков
программирования, но почти все компиляторы транслируют программы в язык
ассемблера. Ассемблер, в свою очередь, транслирует программу в особые
комбинации нулей и единиц - машинный язык (язык машинных команд),
единственный язык, который понимает компьютер. На первых компьютерах
все программы писались на машинном языке, но в 50-х гг. стали
использовать его символический аналог - язык ассемблера.
Давайте посмотрим, каким машинных командам соответствуют команды ассемблера. Рассмотрим простейшую команду:
mov ebx, eax
На машинном языке это будет выглядеть так:
8B D8
Здесь 8B - код операции. Рассмотрим другой пример:
mov ecx, 128
На машинном языке ей соответствует:
B9 00000080
Код операции - B9. Можно сделать вывод, что прямого соответствия между командой ассемблера и соответствующей машинной командой нет. Команда в примере одна и та же, а коды операций машинного языка - разные. Дело в том, что большинство команд ассемблера имеют несколько возможных вариантов сочетания операндов, а машинная команда всегда однозначна по отношению к производимым действиям на уровне аппаратуры. Код операции является номером микропрограммы в блоке микропрограммного управления. Для каждой конкретной команды ассемблера с конкретным вариантом сочетания операндов существует своя микропрограмма и свой код операции.
Любая команда ассемблера содержит несколько элементов:
- Элемент, описывающий, что делать, называется кодом операции (далее КОП).
- Элементы, описывающие объекты, с которыми нужно что-то делать, называются операндами. Операнды могут не задаваться явно, а подразумеваться по умолчанию
- Элементы, описывающие, как делать, называются типами операндов. Они обычно задаются неявно.
Команды передачи управления
Последовательно работает очень мало программ. Обычно в программе есть точки, в которых принимается решение о том, какая команда будет выполняться следующей. Это решение может быть безусловным (управление передается конкретной команде, которая находится на некотором удалении от текущей) и условным (команда, которая будет выполняться следующей, определяется на основе анализа некоторых условий и данных). Но каким же образом обозначается место, куда необходимо передать управление? Оно обозначается с помощью меток. Метка - символическое имя определенной ячейки памяти, предназначенное для использования в командах передачи управления в качестве операнда. Транслятор ассемблера присваивает каждой метке три атрибута:
- Имя сегмента кода, в котором эта метка описана
- Смещение, то есть расстояние в байтах от начала этого сегмента
- Тип метки
Метки бывают двух типов:
- near. Переход на такую метку возможен только в пределах того сегмента кода, в котором она описана
- far. Переход на метку возможен в результате межсегментной передачи управления
Определить метку можно двумя способами:
- с помощью директивы LABEL
- с помощью оператора “:” (двоеточие).
Используя оператор “:”, можно определить метку только ближнего типа (near). Например:
m:
mov eax,1
Используя оператор LABEL, можно определить метку любого типа. Например:
m label near
mov eax,1
или
m label far
mov eax,1
Применение меток оправдано во многих случаях. В одних лучше использовать метки ближнего типа, в других - дальнего. В следующих уроках мы рассмотрим эти случаи и научимся правильно выбирать тип метки.
Синтаксис команды безусловного перехода такой:
jmp [модификатор] адрес_перехода
Существует несколько кодов машинных команд, соответствующих JMP. Они отличаются дальностью перехода и способом задания целевого адреса. Операнд адрес_перехода может находиться в текущем сегменте(близкий переход) кода или в другом (дальний переход). Адресом перехода может являться имя метки:
jmp m
m:
mov eax,1
Есть еще такой вариант (указывается не адрес перехода, а место, где он хранится):
lea eax,m
jmp eax
m:
mov eax,1
Теперь перейдем к командам условного перехода. В таких командах решение о том, какая команда будет выполняться следующей, принимается в зависимости от некоторых условий, различных для разных команд перехода . Таких команд 18, они позволяют проверить:
- отношение операндов со знаком (больше либо меньше)
- отношение операндов без знака (выше или ниже)
- состояние арифметических флагов ZF, SF, CF, OF, PF(но не AF)
Все команды условного перехода имеют одинаковый синтаксис:
jcc метка_перехода
Любая из них начинается с символа “j” (от слова jump), а вместо “cc” указывается конкретное условие, которое должна проанализировать программа. Метка перехода может находиться только в пределах текущего сегмента! Поэтому необходимость в модификаторе отпадает (всегда near). Условие обязательно должно быть сформировано до команды передачи управления. Его источником может служить любая команда, изменяющая состояние арифметических флагов или регистра ECX/CX.
В большинстве случаев Вы будете пользоваться командой CMP. Поэтому сейчас я немного расскажу вам о ней. Команда CMP производит практически те же действия, что и команда SUB, только она не записывает результат вычитания на место первого операнда, а просто устанавливает флаги (нас интересует флаг знака результата, и флаг нулевого результата). Вот ее синтаксис:
cmp операнд_1, операнд_2
Флаги, устанавливаемые командой CMP, можно проанализировать командами условного перехода. Понимание принципа формирования названий команд условного перехода облегчит их запоминание и дальнейшее практическое использование (см. табл. 1).
Мнемоническое обозначение | Оригинальный термин | Перевод | Тип операндов |
---|---|---|---|
e | Equal | Равно | Любые |
n | Not | Нет | Любые |
g | Greater | Больше | Числа со знаком |
l | Less | Меньше | Числа со знаком |
a | Above | Выше(то есть больше) | Числа без знака |
b | Below | Ниже(то есть меньше) | Числа без знака |
Привожу пример программы, которая обнуляет поле pole_m длинной n байт:
.data
n equ 50
pole_m db n dup (?)
.code
;...
xor bx, bx ; очищаем bx
m1:
mov mem[bx], 0 ; заносим в mem[bx] 0
inc bx ; увеличение bx на 1
cmp bx, n ; сравниваем bx с n (вычитаем n из bx)
jne m1 ; если bx не равен n, то повторяем все завново
exit:
;...
В табл. 2 перечислены команды условного перехода, которые можно использовать после команды cmp:
Типа операндов | Мнемокод команды условного перехода |
Критерий условного перехода |
Значения флагов для перехода |
---|---|---|---|
Любые | JE | операнд_1=операнд_2 | ZF=1 |
Любые | JNE | операнд_1<>операнд_2 | ZF=0 |
Со знаком | JL/JNGE | операнд_1<операнд_2 | SF<>OF |
Со знаком | JLE/JNG | операнд_1<=операнд_2 | SF<>OF или ZF=1 |
Со знаком | JG/JNLE | операнд_1>операнд_2 | SF=OF или ZF=0 |
Со знаком | JGE/JNL | операнд_1=>операнд_2 | SF=OF |
Без знака | JB/JNAE | операнд_1<операнд_2 | CF=1 |
Без знака | JBE/JNA | операнд_1<=операнд_2 | CF=1 или ZF=1 |
Без знака | JA/JNBE | операнд_1>операнд_2 | CF=0 и ZF=0 |
Без знака | JAE/JNB | операнд_1=>операнд_2 | CF=0 |
Теперь, используя эту таблицу, Вы сможете с легкостью применять команды условного перехода. На этом мы закончим. Удачи!