Перше, що вам потрібно - це щось на кшталт цього файлу . Це база даних інструкцій для процесорів 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
. Надішліть його на свій вихідний модуль разом із зауваженням, що жоден з байтів не містить посилань на пам'ять (модуль виведення, можливо, повинен знати, чи є)
Повторіть кожну інструкцію. Слідкуйте за мітками, щоб ви знали, що потрібно вставити, коли на них посилаються. Додайте засоби для макросів та директив, які передаються до модулів виведення об'єктного файлу. І це в основному, як працює асемблер.