Структура машинной команды

Источник: Структура машинной команды

Смотрите также страницы справочника:

Байты MRM и SIB - формы адресации

Все коды байта MRM (16 бит)
Все коды байта MRM (32 бит)
Все коды байта SIB

Пояснения к основным таблицам
Команды ассемблера и машинные команды

Примечание. Есть две машинных команды (CALL, код 9A, JMP, код EA), в которых поле для задания адреса занимает 6 байт.

### Структура машинной команды для режима "16 бит"

  0 или 1
  1 или 2
  0 или 1
  0,1 или 2
  0,1 или 2

Примечание. Есть две машинных команды (CALL, код 9A, JMP, код EA), в которых поле для задания адреса занимает 4 байта.

### Префиксы перед кодом операции

Собственно команда начинается с кода операции. Но перед командой может быть префикс. И даже сразу несколько префиксов. Каждый префикс - это один байт.

Вообще-то префиксы перед командами встречаются не часто. Чтобы в этом убедиться, достаточно посмотреть на какую-нибудь распечатку программы в виде машинных команд, например, на результаты работы дизассемблера. Еще более редко можно увидеть сразу два префикса подряд.

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

Если в команде есть более одного префикса, то префиксы должны располагаться в том порядке, в котором они показаны в таблицах, приведенных выше.

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

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

Префикс действует только в пределах той команды, перед которой он стоит.

Код префикса не может совпадать с кодом операции какой-нибудь команды. Это хорошо видно по таблице   Первый байт кода операций.   В этой таблице показаны также и коды префиксов.

### Префиксы команды

"Префиксы команды" - это название для первой группы префиксов. Сразу скажем, что не самое удачное название, так как другие префиксы с другими названиями тоже являются префиксами для команды.

Префиксы команды имеют свои собственные имена на языке ассемблера. И вдобавок, некоторые ассемблеры показывают эти команды в отдельной строке. Поэтому при программировании на ассемблере эти префиксы команды могут восприниматься как самостоятельные команды. (Это еще раз показывает, что мышление на языке ассемблера может сильно отличаться от мышления на языке машинных команд).

Код **F0** - префикс блокировки шины, команда **LOCK** ("lock" - запирать). Этот префикс употребляется только с теми командами, которые поддерживают такую возможность - возможность блокировки шины.

Коды **F2**, **F3** - префиксы повторения, команда **REP** ("repeat" - повторять) и другие команды этой группы. Такие префиксы употребляются только с цепочечными командами. Префиксы группы REP позволяют организовать циклическое выполнение цепочечной команды. Смотрите страницу   Цепочечные команды.

Код **F1** - нет такого префикса и нет такой команды. Тут следует заметить, что это единственный код для первого байта команды, который все еще остается свободным. Поневоле напрашивается мысль о существовании некоторой недокументированной возможности ...

### Префикс изменения размера адреса

Код **67**.

Если общий режим выполнения программы равен "32 бита", то для команды, перед которой есть префикс 67, устанавливается атрибут размера адреса "16" бит.

Если общий режим выполнения программы равен "16 бит", то для команды, перед которой есть префикс 67, устанавливается атрибут размера адреса "32" бита.

Действие префикса зависит от конкретной команды.

### Префикс изменения размера операнда

Код **66**.

Если общий режим выполнения программы равен "32 бита", то для команды, перед которой есть префикс 66, устанавливается атрибут размера операнда "16" бит.

Если общий режим выполнения программы равен "16 бит", то для команды, перед которой есть префикс 66, устанавливается атрибут размера операнда "32" бита.

Действие префикса зависит от конкретной команды.

### Префиксы замены сегмента

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

Код **26** - сегмент по умолчанию заменяется на сегмент **ES**.
Код **2E** - сегмент по умолчанию заменяется на сегмент **CS**.
Код **36** - сегмент по умолчанию заменяется на сегмент **SS**.
Код **3E** - сегмент по умолчанию заменяется на сегмент **DS**.
Код **64** - сегмент по умолчанию заменяется на сегмент **FS**.
Код **65** - сегмент по умолчанию заменяется на сегмент **GS**.

См. также страницу   Коды сегментных регистров.

### Код операции

После всех префиксов (если перед командой есть префиксы) начинается собственно машинная команда. И начинается машинная команда с кода операции.

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

Код операции (сокращенно - КОП) может состоять из одного байта или из двух байт. Если первый байт кода операции имет значение **0F**, то в этом коде операции есть еще и второй байт.

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

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

Первый байт кода операций
Второй байт кода операцийВ таблицах справочника в колонке "Формат" основной байт кода операции условно показан в виде восьми черточек, соотвествующих восьми разрядам основного байта.
Это выглядит так: ( **--------** ).

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

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

Например, обозначение ( **-------w** ) показывает, что в нулевом (самом младшем) разряде основного байта имеется особый бит ( w ), буква "w" означает "word" - "слово". И это значит, что данная команда имеет два варианта, которые отличаются размером операнда.

Разберем какой-нибудь конкретный случай. Пусть это будет команда ADD с кодами 04 и 05, которая выполняет операцию сложения. Двоичная запись для этих кодов операции выглядит так: 00000100 и 00000101, отличие только в самом младшем разряде.

Здесь вариант команды ADD с кодом 04 ( w = 0 ) выполняет операцию сложения для байта (размер операнда 8 бит), а вариант с кодом 05 ( w = 1 ) выполняет ту же операцию для слова (размер 16 бит).

Но столь просто все было для 16-битных процессоров. С появлением 32-битных ситуация усложнилась. Теперь эта же команда может выполнить свою операцию для байта (8 бит), слова (16 бит), двойного слова (32 бита). Вариант команды с ( w = 0 ) остался прежним, всегда работает только для байта (8 бит). Но работа команды в варианте с ( w = 1 ) будет зависеть от установленного атрибута размера операнда, для слова (16 бит) или для двойного слова (32 бита). Посмотрите выше про префикс изменения размера операнда - код 66, который делает такое переключение для одной команды.

Обозначение ( **-----reg** ) говорит о том, что прямо в основном байте кода операции задается регистр, для которого эта операция выполняется.

И это значит, что для данной команды имеется восемь разных вариантов с разными кодами операций. Примерами таких команд могут служить однобайтные команды INC и DEC (инкремент и декремент).

Можно посмотреть и с другой стороны, со стороны команд ассемблера. Если для команды INC задается в качестве операнда какой-нибудь регистр, например EAX или EBX, то операнд будет указан прямо в коде операции машинной команды.

Полный список обозначений для особых битов и битовых полей основного байта приведен на странице   Пояснения к основным таблицам.

И еще одно замечание про код операции. Код операции - один или два байта в начале команды - можно называть основным кодом операции. Так как бывает еще и дополнительный код операции. Дело в том, что в системе команд x86 имеются довольно много таких случаев, когда основной код операции еще не задает полностью, какая именно операция должна выполняться. В этих особых случаях к основному коду операции присоединяются еще три дополнительных бита. Подробнее об этом рассказано ниже, в разделе "Обозначение NNN".

### Байт MRM - (mod,reg,r/m)

Байт MRM, полное название ( mod,reg,r/m ) - это байт режима адресации.

Полное название выглядит именно так - "байт ( mod,reg,r/m )". А сокращенное название в разных книгах выглядит по-разному. В нашем справочнике применяется сокращенное название "байт MRM".

Байт MRM имеется не во всех командах. Это определяется конкретным кодом операции (а говоря точнее, "основным кодом операции"), входит ли байт MRM в состав данной машинной команды или не входит.

Здесь важно заметить, что речь идет не о команде с точки зрения языка ассемблера, а о машинной команде с конкретным основным кодом операции. Например, команда сложения ADD имеет четыре разных варианта с байтом MRM, еще четыре варианта с "сокращенным" байтом MRM, который в нашем справочнике обозначен как NNN, и два варианта вообще без байта MRM. Итого это получается десять разных кодов операции, десять разных вариантов машинной команды. См. страницу   Основные команды арифметики.

В таблицах справочника, в колонке "Формат" показано, в составе каких команд есть байт MRM. Причем наличие в колонке "Формат" обозначения MRM или обозначения NNN говорит о том, что в составе команды есть целая группа байтов, относящаяся к MRM. Первым в этой группе идет байт ( mod,reg,r/m ), то есть, собственно байт MRM, затем, если требуется, может идти второй байт режима адресации, это байт SIB, а затем, если требуется, может быть еще и поле для задания адреса.

Байт MRM делится на три битовых поля: двухбитовое поле ( mod ), трехбитовое поле ( reg ), трехбитовое поле ( r/m ).

 mod

Поле ( reg ) определяет первый операнд команды, операнд-приемник (destination). Поле ( r/m ) определяет второй операнд команды, операнд источник (source). Обычно бывает именно такое распределение ролей между ( reg ) и ( r/m ), который из операндов будет источником, а который приемником. Но бывает и наоборот, это зависит от конкретной команды.

Поле ( reg ) задает регистр общего назначения. Три бита - это восемь вариантов, восемь разных регистров.

Поле ( r/m ) задает либо регистр, либо память. Это поле действует совместно с полем ( mod ), вместе будет пять битов и получается 32 варианта, это дает 8 вариантов для задания регистров и 24 варианта для задания формы и режима адресации памяти.

В режиме "32 бита" после байта MRM может стоять еще и второй байт режима адресации - байт SIB, так что количество разных форм задания адреса памяти еще более возрастает.

### Обозначение NNN

В системе команд x86 имеются такие команды, в которых байт MRM используется несколько иначе, чем было описано выше. Отличие в том, как трактуется поле ( reg ).

Дело в том, что основной код операции (один байт или два байта) не всегда однозначно определяет, какая это команда. При некоторых значениях основного кода операции к этому коду операции добавляются еще три бита из поля ( reg ) байта MRM. И тогда уже этот рассширенный код операции определяет команду.

Как пример такого случая, можно назвать команды с первым байтом кода операции равным **80**, **81**, **82**, **83**. См. страницу   Первый байт кода операций.

В таблицах данного справочника для подобных команд ставится обозначение **NNN** в колонке "Формат" вместо обозначения MRM. Просто нужно было как-то выделить отдельно этот особый случай. Вот и получился "байт NNN" вместо "байта MRM".

Обозначение NNN показывает, что в команде стоит сокращенный вариант байта MRM, который может задавать только один операнд, вместо прежних двух операндов. Этот операнд определяется полями ( mod ) и ( r/m ), так что операнд может быть либо регистром, либо может иметь одну из многих форм задания адреса в памяти.

В системе команд x86 есть случаи, когда байт MRM применяется в сокращенном виде только из-за того, что для данной конкретной команды не требуется, чтобы байт MRM задавал два операнда, так как в этой команде нужен только один операнд. В этом случае поле ( reg ) байта MRM просто не используется, а в документации оговаривается, что всегда должно быть ( reg = 0 ).

Таких случаев совсем немного:

* команда MOV, коды C6 и C7
* команда POP, код 8F
* группа команд SET(cond), коды от 0F 90 до 0F 9F

Для этих случаев в таблицах справочника тоже ставится обозначение NNN.

### Байт SIB - (scale,index,base)

Байт SIB - это второй байт режима адресации.

Байт SIB возможен в команде только в режиме 32 бита и только в том случае, когда в команде уже есть байт MRM (в полной форме или в сокращенной форме NNN).

Этот дополнительный байт для задания режима адресации включается в состав команды только при некоторых определенных значениях в полях ( mod ) и ( r/m ) байта MRM.

Аналогично байту MRM, байт SIB делится на три битовых поля:

 scale

Байт SIB позволяет применять еще более сложные формы задания адреса в памяти.

### Поле для задания адреса

В этом поле из 1, или 2, или 4 байт, расположенном внутри команды, может быть задан либо полный адрес, либо смещение относительно некоторого адреса.

С точки зрения терминологии, не всегда можно отличить "смещение" от "полного адреса". Например, как называть полный адрес внутри сегмента, если такой адрес всегда задается смещением от начала сегмента.

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

* Если в формат команды входит байт MRM, то поле для задания адреса (если оно есть) является приложением к байту MRM, оно может потребоваться в разных формах задания адреса, применяемых в байте MRM. Причем от кода байта MRM зависит, будет ли в данной машинной команде поле для задания адреса или не будет.
* Если в формате команды нет байта MRM, то поле для задания адреса используется в той конкретной форме, которая предписана для данной конкретной машинной команды.

Эти два разных случая по-разному отражаются в таблицах справочника в колонке "Формат".

Если в формате команды есть обозначение MRM (или обозначение NNN), то дополнительные байты, относящиеся к байту MRM никак не показываются. Так что поле для задания адреса в этом случае показано не будет.

Если в формате команды нет байта MRM, но есть поле для задания адреса, то в колонке "Формат" будет стоять обозначение "**addr (..)**", где в скобках указывается количество байт.

В системе команд x86 невозможен случай, чтобы в некоторой команде оказалось сразу два поля для задания адреса. Поэтому в таблицах справочника невозможен случай, чтобы в колонке "Формат" стояло обозначение MRM (или NNN), а затем еще и обозначение "addr (..)".

Поле для задания адреса есть не во всех машинных командах.

### Непосредственный операнд

Форматы многих команд предусматривают возможность задавать константу непосредственно внутри команды. На языке ассемблера это соответствует заданию для операнда конкретного численного значения вместо имени переменной.

В таблицах справочника в колонке "Формат" наличие непосредственного операнда показано обозначением "**data (..)**", где в скобках указывается количество байт.

Поле для непосредственного операнда есть не во всех машинных командах.

Если в формате команды есть поле для задания непосредственного операнда, то это поле в команде всегда будет последним.