Первое, что вам нужно, это что-то вроде этого файла . Это база данных команд для процессоров x86, используемая ассемблером NASM (которую я помогал написать, хотя не части, которые фактически переводят инструкции). Давайте выберем произвольную строку из базы данных:
ADD rm32,imm8 [mi: hle o32 83 /0 ib,s] 386,LOCK
Это означает, что оно описывает инструкцию ADD
. Существует несколько вариантов этой инструкции, и конкретный описываемый здесь вариант - это вариант, который принимает либо 32-битный регистр, либо адрес памяти и добавляет немедленное 8-битное значение (т. Е. Константу, непосредственно включенную в инструкцию). Пример инструкции по сборке, которая будет использовать эту версию:
add eax, 42
Теперь вам нужно взять текстовый ввод и разбить его на отдельные инструкции и операнды. Для вышеприведенной инструкции это, вероятно, приведет к структуре, которая содержит инструкцию ADD
и массив операндов (ссылка на регистр EAX
и значение 42
). Получив эту структуру, вы пробегаете базу данных команд и находите строку, которая соответствует как имени инструкции, так и типам операндов. Если вы не нашли соответствия, это ошибка, которая должна быть представлена пользователю («недопустимая комбинация кода операции и операндов» или подобный - обычный текст).
Как только мы получили строку из базы данных, мы смотрим на третий столбец, который для этой инструкции:
[mi: hle o32 83 /0 ib,s]
Это набор инструкций, которые описывают, как генерировать инструкцию машинного кода, которая требуется:
- Это
mi
описание операндов: один modr/m
операнд (регистр или память) (что означает, что нам нужно добавить modr/m
байт в конец инструкции, к которой мы придем позже), а другой - немедленную инструкцию (которая будет использоваться в описании инструкции).
- Дальше есть
hle
. Это определяет, как мы обрабатываем префикс «блокировки». Мы не использовали «замок», поэтому игнорируем его.
- Дальше есть
o32
. Это говорит нам о том, что если мы собираем код для 16-битного выходного формата, инструкции требуется префикс переопределения размера операнда. Если бы мы производили 16-битный вывод, мы бы сейчас создали префикс ( 0x66
), но я предполагаю, что нет, и продолжаем.
- Дальше есть
83
. Это буквенный байт в шестнадцатеричном формате. Мы выводим это.
Дальше есть /0
. Это определяет некоторые дополнительные биты, которые нам понадобятся в байте modr / m, и заставляет нас их генерировать. modr/m
Байт используется для регистров кодируют или ссылки косвенных памяти. У нас есть один такой операнд, регистр. У регистра есть номер, который указан в другом файле данных :
eax REG_EAX reg32 0
Мы проверяем, что reg32
соответствует требуемому размеру инструкции из исходной базы данных (это делает). Это 0
номер регистра. modr/m
Байт представляет собой структуру данных , указанная с помощью процессора, который выглядит следующим образом :
(most significant bit)
2 bits mod - 00 => indirect, e.g. [eax]
01 => indirect plus byte offset
10 => indirect plus word offset
11 => register
3 bits reg - identifies register
3 bits rm - identifies second register or additional data
(least significant bit)
Поскольку мы работаем с регистром, mod
поле есть 0b11
.
reg
Поле номер регистра , который мы используем,0b000
- Поскольку в этой инструкции есть только один регистр, нам нужно чем-то заполнить
rm
поле. Для этого и были указаны дополнительные данные /0
, поэтому мы поместили их в rm
поле 0b000
.
modr/m
Байт , следовательно , 0b11000000
или 0xC0
. Мы выводим это.
- Дальше есть
ib,s
. Это указывает подписанный немедленный байт. Мы смотрим на операнды и отмечаем, что у нас есть непосредственное доступное значение. Мы конвертируем его в подписанный байт и выводим его ( 42
=> 0x2A
).
Полная инструкция собран поэтому: 0x83 0xC0 0x2A
. Отправьте его в модуль вывода вместе с примечанием о том, что ни один из байтов не является ссылками на память (модуль вывода может знать, если они это делают).
Повторите для каждой инструкции. Следите за метками, чтобы вы знали, что вставлять, когда на них есть ссылки. Добавьте средства для макросов и директив, которые передаются в модули вывода ваших объектных файлов. И это в основном так, как работает ассемблер.