Как процессор / ассемблер узнает размер следующей инструкции?


Для примера представьте, что я строю виртуальную машину. У меня есть массив байтов и цикл while, как узнать, сколько байтов нужно прочитать из массива байтов для следующей инструкции, чтобы интерпретировать инструкцию intel 8086 like?

Правка: (комментируется) процессор считывает код операции по указателю инструкции, с 8086 и CISC у вас есть один байт и два байта инструкций. Как узнать, будет ли следующая инструкция F или FF?

Править: Нашел себе анзю в этом пейсе текста на http://www.swansontec.com/sintel.html

Код операции, или код операции, следует за любыми необязательными префиксами. То опкод сообщает процессору, какую команду Выполнить. Кроме того, опкоды содержат битовые поля, описывающие размер и тип операндов, к которым ожидать. Инструкция NOT, например, имеет код операции 1111011w. In этот код операции, бит w определяет, является ли операнд байтом или слово. Инструкция OR имеет код операции 000010dw. В этом коде операции, бит d определяет, какие операнды являются источником и назначением, и бит w снова определяет размер. Некоторые инструкции имеют несколько разные операционные коды. Например, когда или используется с аккумулятором регистр (AX или EAX) и константа, он имеет специальную экономию места код операции 0000110w, что исключает необходимость в отдельном байте ModR/M. С точки зрения размерного кодирования запоминание точных битов кода операции не является необходимый. Имея общее представление о том, какие типы опкодов доступны для конкретного наставления это важнее.

2 5

2 ответа:

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

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

TLDR:

Решение является более сложным, чем массив фиксированного размера.


Все дело в контексте, вот почему дизассемблеры, такие как IDA, имеют сложные алгоритмы для этого.

Инструкции имеют переменную длину для x86. Но если вы знаете начало инструкции, вы знаете, где она заканчивается. Из-за этого вы можете знать, где начинается следующий. Я скоро объясню исключения. Но сначала, вот вам пример. пример:

ASM:
mov eax, 0
xor eax, eax

Machine:
b8 00 00 00 00
31 c0

Пояснение:

Переход к eax - это B8, за которым следует 32-битное (4-байтовое) значение для перехода в eax (поскольку eax-32-бит). Другими словами, mov eax, immediate всегда будет 5 байт. Таким образом, если вы знаете, что вы начинаете с инструкции (не всегда безопасное предположение), и Байт B8, Вы знаете, что это 5-байтовая инструкция, и что следующая инструкция должна начинаться на 5 байт позже.

Обратите внимание, что обе инструкции (mov eax, 0 и xor eax, eax) эффективно делают одно и то же, clear eax до 0.

Исключение:

С прыжками / вызовами могут возникнуть сложности. Можно перейти в адресное пространство, которое находится в "середине инструкции"... но все равно казнить.

Давайте посмотрим на:

mov eax, 0x90909090

Машинный код:

b8 90 90 90 90

Если бы у нас позже была команда jmp, которая прыгнула бы в адрес 3-го байта вышеупомянутой инструкции (где-то в середине ее), она просто сделала бы 3 NOPs (no operation) и упала бы на следующую инструкцию после it (не устанавливая eax на 0x90909090). Это происходит потому, что NOP является 1-байтовой инструкцией, состоящей из 0x90.