Эта статья начинает цикл, посвященный более глубокому изучению ассемблера. Теперь каждая статья будет разделена на две части. В первой части будут рассматриваться некоторые теоретические свнедения, а во второй - новые команды, примеры кода и приемы программирования на ассемблере.

Формат машинных команд 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).

Таблица 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:

Таблица 2. Команды условного перехода

Типа операндов Мнемокод команды
условного перехода
Критерий условного
перехода
Значения флагов
для перехода
  Любые   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

Теперь, используя эту таблицу, Вы сможете с легкостью применять команды условного перехода. На этом мы закончим. Удачи!

Автор: miksayer