Глава IV — Програмен модел и система команди на x86 микропроцесорите
1. Програмен модел. Регистри с общо предназначение
Програмният модел дефинира какво е достъпно за програмиста: кои регистри съществуват, колко бита са, и за какво служат. Всичко останало (кеш, TLB, конвейер) е скрито от програмата.
32-битов програмен модел (IA-32)
Програмно достъпните регистри в защитен 32-битов режим:
Регистри с общо предназначение (GPR) — 8 × 32 бита:
Всеки регистър е достъпен като цял (32 бита) или чрез именувани части:
63 32 31 16 15 8 7 0 ┌────────────────┬─────────────────┬────────┬───────┐ │ (само x64) │ EAX │ AH │ AL │ └────────────────┴────────┬────────┴────────┴───────┘ │ AX (16 бита) │ EAX (32 бита) │ RAX (64 бита, само x64)| 64-бита (x86-64) | 32-бита | 16-бита | 8H (15–8) | 8L (7–0) | Предназначение |
|---|---|---|---|---|---|
| RAX | EAX | AX | AH | AL | Акумулатор — резултат от операции, I/O |
| RBX | EBX | BX | BH | BL | Базов регистър за адресиране |
| RCX | ECX | CX | CH | CL | Брояч — LOOP, REP, SHL CL |
| RDX | EDX | DX | DH | DL | Данни — разширение на EAX при MUL/DIV |
| RSI | ESI | SI | — | SIL | Source Index — изходна позиция при стрингови операции |
| RDI | EDI | DI | — | DIL | Destination Index — целева позиция при стрингови операции |
| RSP | ESP | SP | — | SPL | Stack Pointer — върхът на стека (не се ползва свободно!) |
| RBP | EBP | BP | — | BPL | Base Pointer — база на стековия кадър при процедури |
Указател на инструкциите:
EIP[31..0] — адресът на следващата инструкция за изпълнение (не се чете/пише директно)
Регистър на флаговете:
EFLAGS[31..0] — битов регистър: статусни флагове (CF, ZF, SF, OF), управляващи (DF), системни (IF, TF, IOPL)
Сегментни регистри (6 × 16 бита):
CS(Code Segment) — сегментът на кода;DS(Data) — данни;SS(Stack) — стекES,FS,GS— допълнителни данни (FS/GS се ползват за thread-local/per-CPU данни от ОС)
64-битов програмен модел (Intel 64 / AMD64)
16 × 64-битови GPR — RAX–RBP са разширения на IA-32 регистрите; R8–R15 са нови:
| 64-бита | 32-бита | 16-бита | 8-бита | Бележки |
|---|---|---|---|---|
| RAX–RBP | EAX–EBP | AX–BP | AL/AH–BPL | Разширения на IA-32 (8 регистъра) |
| R8–R15 | R8D–R15D | R8W–R15W | R8B–R15B | Нови в x86-64 (8 регистъра) |
| RIP | EIP | — | — | Указател на инструкциите |
| RFLAGS | EFLAGS | FLAGS | — | Флагов регистър |
Достъп към части на 64-битовите регистри: RAX[63..0] → EAX[31..0] → AX[15..0] → AH[15..8]/AL[7..0]; аналогично за R8 → R8D → R8W → R8B.
Важно правило: Запис в 32-битов подрегистър (напр.
MOV EAX, 1) автоматично зануля горните 32 бита (RAX[63..32] = 0). Запис в 8/16-битови подрегистри не зануля горните битове — старите стойности остават!
XMM/YMM/ZMM регистри: виж Глава III (SIMD)
2. Система команди. Общ формат на x86 команда
Формат на инструкция (променлива дължина: 1–15 байта)
x86 инструкцията не е с фиксирана дължина — всяко поле е незадължително или с различен размер. Процесорът разчита (декодира) инструкцията последователно, поле по поле.
┌──────────┬─────┬────────┬────────┬─────┬──────────────┬───────────┐│ Prefixes │ REX │ Opcode │ ModR/M │ SIB │ Displacement │ Immediate ││ 0–4 B │ 0/1 │ 1–3 B │ 0/1 B │0/1B │ 0/1/2/4 B │ 0/1/2/4B │└──────────┴─────┴────────┴────────┴─────┴──────────────┴───────────┘| Поле | Размер | Задължително? | Роля |
|---|---|---|---|
| Prefixes | 0–4 байта | Не | Модифицира поведението на инструкцията |
| REX | 1 байт | Само в 64-bit режим | Разширява регистровите полета до R8–R15 |
| Opcode | 1–3 байта | Да | Кодът на операцията |
| ModR/M | 1 байт | При операнди | Определя регистър и/или начин на адресиране |
| SIB | 1 байт | При сложно адресиране | Scale + Index + Base за [reg + reg×n + disp] |
| Displacement | 0 / 1 / 2 / 4 байта | При памет + отместване | Числово отместване спрямо базовия регистър |
| Immediate | 0 / 1 / 2 / 4 байта | При непосредствена стойност | Буквална стойност, вградена в инструкцията |
Префикси (Prefixes)
| Група | Примери | Функция |
|---|---|---|
| Група 1 | LOCK, REP, REPE, REPNE | Заключване на шината / повторение на стрингова операция |
| Група 2 | CS:, SS:, DS:, ES:, FS:, GS: | Замества подразбирания сегментен регистър |
| Група 3 | 0x66 | Замества размера на операнда (32↔16 бита) |
| Група 4 | 0x67 | Замества размера на адреса (32↔16 бита) |
REX Prefix (само в 64-битов режим)
Байт с формат 0100WRXB:
- W=1: операцията работи с 64-битови операнди (иначе 32-битови)
- R: разширява полето REG в ModR/M — дава достъп до R8–R15
- X: разширява INDEX полето в SIB
- B: разширява R/M или BASE в SIB
Пример: MOV RAX, R10 изисква REX.W=1 (64-бита) и REX.R=1 (R10 в REG поле) + REX.B=1 (RAX в R/M).
Opcode (1–3 байта)
Кодът на операцията. Може да съдържа вграден код на регистър в долните 3 бита (напр. B8+rd за MOV reg, imm32).
ModR/M байт
Управлява избора на регистър и режима на адресиране:
Bits 7–6: Mod 00=памет без disp | 01=памет+disp8 | 10=памет+disp32 | 11=само регистърBits 5–3: Reg регистър (src или dst) или разширение на opcodeBits 2–0: R/M регистър или адресиращ режим (при Mod≠11 → указва паметта)Пример: MOV EAX, [EBX+8]
- Mod=01 (памет + disp8), Reg=EAX (000), R/M=EBX (011)
- Displacement = 0x08
SIB (Scale-Index-Base) байт
Присъства само когато R/M=100 и Mod≠11. Позволява сложно адресиране:
Bits 7–6: Scale 00=×1 | 01=×2 | 10=×4 | 11=×8Bits 5–3: Index регистър-индекс (ESP=100 означава "без индекс")Bits 2–0: Base регистър-база
Ефективен адрес = Base + (Index × Scale) + DisplacementПример: MOV EAX, [EBX + ESI×4 + 100h] → SIB: Scale=10(×4), Index=ESI, Base=EBX, Disp=0x100
Displacement и Immediate
- Displacement: отместване (0, 8 или 32 бита) — добавя се към адреса от ModR/M или SIB
- Immediate: буквална стойност (0, 8, 16 или 32 бита) — директно вградена в машинния код
3. Основни групи x86 команди
3.1 Команди за пренос на данни
| Инструкция | Действие |
|---|---|
MOV dst, src | Копиране (регистър↔регистър, регистър↔памет) |
MOVZX dst, src | Копиране с нулево разширение (напр. AL → EAX, горните битове = 0) |
MOVSX dst, src | Копиране със знаково разширение (знаковият бит се разпространява нагоре) |
XCHG op1, op2 | Атомарна размяна (с LOCK — безопасна при мултипроцесорна среда) |
PUSH src | Записва в стека: ESP −= 4, след това [ESP] = src |
POP dst | Чете от стека: dst = [ESP], след това ESP += 4 |
LEA dst, [mem] | Зарежда адреса (не съдържанието!) — полезно за изчисляване на указатели |
IN AL, port / OUT port, AL | Вход/изход от I/O порт |
3.2 Аритметични команди
| Инструкция | Действие |
|---|---|
ADD dst, src | Събиране; задава CF, ZF, SF, OF |
ADC dst, src | Събиране + CF (за многоточна аритметика: 64-бита в 32-бит. режим) |
SUB dst, src | Изваждане |
SBB dst, src | Изваждане − CF (заем при многоточна аритметика) |
INC op / DEC op | +1 / −1 (не засяга CF — внимание при проверка за пренос!) |
NEG op | Смяна на знак (двоично допълнение) |
MUL op | Беззнаково умножение: AX = AL×op (8b) или EDX:EAX = EAX×op (32b) |
IMUL op | Знаково умножение (1, 2 или 3 операнда) |
DIV op | Беззнаково деление: AL=AX/op, AH=AX mod op |
IDIV op | Знаково деление |
CMP op1, op2 | Изваждане без запис — само задава флагове за Jcc |
3.3 Логически и битови команди
| Инструкция | Действие |
|---|---|
AND, OR, XOR, NOT | Побитови операции; AND/OR/XOR нулират CF и OF |
SHL/SAL, SHR, SAR | Логическо отместване наляво/надясно; SAR запазва знака |
ROL, ROR, RCL, RCR | Ротация (с или без CF) |
BT, BTS, BTR, BTC | Тест / задаване / нулиране / инверт на отделен бит; резултатът в CF |
BSF, BSR | Сканиране за първи 1-бит (напред/назад); ZF=1 ако операндът е нула |
TEST op1, op2 | AND без запис — само задава ZF/SF/PF (не записва резултата) |
3.4 Команди за управление на потока
| Инструкция | Действие |
|---|---|
JMP label | Безусловен преход (near = в сегмента, far = между сегменти) |
Jcc label | Условен преход по флагове: JZ/JE (ZF=1), JNZ/JNE (ZF=0), JG, JL, JGE, JLE… |
CALL proc | Извикване на процедура: [стек] ← EIP, след това JMP proc |
RET / RETF | Връщане от процедура (near / far) |
LOOP / LOOPE / LOOPNE | ECX−=1; прескача ако ECX≠0 (+ условие по ZF при LOOPE/LOOPNE) |
INT n | Програмно прекъсване (вектор n) — извиква OS или BIOS функция |
INTO | Прекъсване при OF=1 |
IRET / IRETD | Връщане от обработчик на прекъсване (зарежда EIP, CS, EFLAGS от стека) |
3.5 Стрингови команди
MOVS, CMPS, SCAS, LODS, STOS работят с ESI (изход) и EDI (вход):
- С
REP— повтаря ECX пъти - С
REPE/REPNE— повтаря докато ZF=1 / ZF=0 (и ECX > 0) - Посоката зависи от DF:
CLD→ ESI/EDI нарастват;STD→ намаляват
3.6 Системни команди (привилегировани)
Само от ниво 0 (ядрото на ОС):
LGDT, LIDT, LLDT, LTR — зареждат системни дескрипторни таблици
MOV CRx — четене/запис на управляващи регистри
WRMSR, RDMSR — запис/четене на MSR регистри
INVLPG — инвалидира TLB запис за страница
HLT — спира процесора до следващото прекъсване
WBINVD — записва и инвалидира кеша
4. Организация на адресното пространство в 32- и 64-битов режим. Линейни и физически адреси
x86 използва многостепенна адресна трансформация: програмата работи с логически адреси, процесорът ги превежда в линейни (чрез сегментация), а после в физически (чрез странициране).
32-битов режим (Protected Mode)
Трипластова адресна трансформация:
Логически адрес Линеен адрес Физически адрес[Selector : Offset] → [0 … 4 GB] → [0 … 4 GB (max 64 GB с PAE)] (Сегментация) (Странициране)Стъпка 1 — Сегментация:
Линеен адрес = Сегментна база (от GDT/LDT) + Offset
При плоско адресно пространство (flat model) базата е 0 → линеен = offset.
Стъпка 2 — Странициране (ако CR0.PG=1):
Физически адрес = Page Table Entry [линеен адрес / 4096] + (линеен адрес mod 4096)
Адресни пространства:
- Линейно: 2³² = 4 GB (при 32-битов режим)
- Физическо: 2³² = 4 GB (без PAE) или 2³⁶ = 64 GB (с PAE, CR4.PAE=1)
64-битов режим (Long Mode / Intel 64)
Двупластова адресна трансформация:
Виртуален адрес (48 бита канонична форма) → Физически адрес (до 52 бита) (4-нивово странициране: PML4 → PDPT → PD → PT → страница)Канонична форма на 64-битов адрес:
Само 48 бита [47..0] носят информация. Битовете [63..48] трябва да са знаково разширение на бит 47:
- Бит 47 = 0 → биткове [63..48] = 0 → потребителско пространство
- Бит 47 = 1 → биткове [63..48] = 1 → адресно пространство на ядрото
Потребителски: 0x0000_0000_0000_0000 – 0x0000_7FFF_FFFF_FFFFЯдро: 0xFFFF_8000_0000_0000 – 0xFFFF_FFFF_FFFF_FFFF (неканоничната "дупка" между тях е невалидна)Адресни пространства в 64-битов режим:
- Виртуално: 2⁴⁸ = 256 TB (с PML4 странициране)
- Физическо: до 2⁵² = 4 PB (при MAXPHYADDR=52)
Изчисляване на ефективния (логически) адрес
Адресиращите режими определят как процесорът изчислява адреса в паметта:
| Режим | Пример | Формула |
|---|---|---|
| Непосредствен | [1000h] | EA = 1000h |
| Регистров | [EBX] | EA = EBX |
| Базов + отместване | [EBP + 8] | EA = EBP + 8 |
| Индексен | [ESI × 4] | EA = ESI × 4 |
| Базов + индексен | [EBX + ESI] | EA = EBX + ESI |
| Пълен SIB | [EBX + ESI×4 + 100h] | EA = EBX + ESI×4 + 100h |
Пример за пълна трансформация в 32-битов режим:
Инструкция: MOV EAX, [EBX + ESI×4 + 100h] Ефективен адрес = EBX + ESI×4 + 100h (от SIB + Displacement) Линеен адрес = DS.Base + ефективен адрес (от Сегментация) Физически адрес = Page Table[линеен >> 12] × 4096 + (линеен & 0xFFF)Резюме за изпита
- 32-битов модел: 8 GPR (EAX–EBP), EIP, EFLAGS, 6 сегментни регистри
- 64-битов модел: 16 GPR (RAX–R15, 64 бита), RIP, RFLAGS; записът в 32-битов подрегистър нулира горните 32 бита
- Инструкция: Prefixes + REX (само x86-64) + Opcode + ModR/M + SIB + Disp + Imm (1–15 байта)
- REX.W=1 → 64-битов операнд; REX.R/X/B → достъп до R8–R15
- ModR/M: Mod(2b) + Reg(3b) + R/M(3b) — определя режима на адресиране
- SIB: Scale(2b) + Index(3b) + Base(3b) → EA = Base + Index×Scale + Disp
- Линеен адрес = Сегментна база + Ефективен адрес; Физически = след странициране
- В 64-битов режим: виртуален адрес е 48-битов (канонична форма), физически до 52 бита
Източници:
- X86-64 Instruction Encoding — OSDev Wiki
- ModR/M — Wikipedia
- Intel 64 and IA-32 Architectures Software Developer’s Manual, Vol. 1 (Basic Architecture), Vol. 2 (Instruction Set Reference)
- Рускова Н. Микропроцесорни системи. ТУ-Варна, 1999