Az x86 assembly nyelv egy alacsony szintű programozási nyelvcsalád, amelyet az x86 processzorosztályú tárgykódok létrehozására használnak. A nyelvcsalád korábbi fejlesztései visszafelé kompatibilitást biztosítottak a CPU-kkal, egészen az 1972 áprilisában bemutatott Intel 8008 mikroprocesszorig visszamenőleg. Assembly nyelvként szorosan kapcsolódnak az architektúra gépi kódú utasításaihoz, lehetővé téve a hardver feletti precíz vezérlést.

Az x86 assembly nyelvekben a mnemonikokat az alapvető CPU-utasítások ábrázolására használják, így a kód ember által olvashatóbb a nyers gépi kódhoz képest. Minden gépi kódú utasítás egy opkód, amelyet az assemblyben egy mnemonik helyettesít. Minden mnemonik a processzor által végrehajtott alapvető műveletnek felel meg, például aritmetikai számításoknak, adatmozgatásnak vagy vezérlési folyamattal kapcsolatos döntéseknek. Az assembly nyelveket leggyakrabban olyan alkalmazásokban használják, ahol a teljesítmény és a hatékonyság kritikus fontosságú. Ide tartoznak a valós idejű beágyazott rendszerek, az operációs rendszer kernelek és az eszközillesztők, amelyek mindegyike a hardvererőforrások közvetlen manipulálását igényelheti.

Ezenkívül a magas szintű programozási nyelvek fordítói néha assembly kódot generálnak köztes lépésként a fordítási folyamat során. Ez lehetővé teszi az assembly szintű optimalizálást, mielőtt előállítaná a processzor által végrehajtandó végső gépi kódot.

Mnemonik és opkódok Az x86 assembly nyelv minden utasítását egy mnemonik képviseli, amely gyakran egy vagy több operandussal kombinálva egy vagy több bájttá, úgynevezett opkóddá alakítja át. Például a NOP utasítás a 0x90 opkóddá, a HLT utasítás pedig a 0xF4 hexadecimális opkóddá alakul. Léteznek olyan opkódok is, amelyekhez nem tartozik dokumentált mnemonik, és amelyeket a különböző processzorok eltérően értelmezhetnek. Az ilyen opkódok használata a program inkonzisztens viselkedését vagy akár kivételek generálását okozhatja egyes processzorokon.

Szintaxis Az x86 assembly nyelvnek két fő szintaxisa van: az Intel szintaxisa és az AT&T szintaxisa. Az Intel szintaxisa a DOS és Windows környezetekben domináns, míg az AT&T szintaxisa a Unix-szerű rendszerekben, mivel a Unixot eredetileg az AT&T Bell Labs fejlesztette ki. Az alábbiakban összefoglaljuk az Intel és az AT&T szintaxis közötti főbb különbségeket:

AT&T Intel Paraméter sorrend movl $5, %eax Forrás a cél előtt. mov eax, 5 A cél a forrás előtt. Paraméter mérete addl $0x24, %esp movslq %ecx, %rax paddd %xmm1, %xmm2 A mnemonikokat egy betű egészíti ki, amely az operandusok méretét jelzi: q a qword (64 bit), l a long (dword, 32 bit), w a word (16 bit), és b a byte (8 bit) esetén. add esp, 24h movsxd rax, ecx paddd xmm2, xmm1 A használt regiszter nevéből származik (pl. rax, eax, ax, al rendre q, l, w, és b értékeket jelöl). Ha a forrás vagy a cél nem regiszter, a paraméter mérete nem következtethető ki, és explicit módon kell deklarálni.

sub byte ptr [esi], 20h

A szélesség alapú nevek továbbra is megjelenhetnek az utasításokban, ha azok egy másik műveletet definiálnak. A MOVSXD a MOVSX-szel ellentétben dword bemenettel rendelkező előjel-kiterjesztésre utal. A SIMD regiszterek szélességgel elnevezett utasításokkal rendelkeznek, amelyek meghatározzák a regiszter felosztásának módját. Az AT&T általában változatlanul hagyja a neveket, így a PADDD-t nem nevezik át "paddl"-re. Jelek A konstans értékek előtagja "$", a regisztereké "%". Az assembler automatikusan felismeri a szimbólumok típusát; azaz, hogy regiszterek, konstansok vagy valami más. Effektív címek movl offset(%ebx, %ecx, 4), %eax Általános szintaxisa displacement(base, index, scale). mov eax, [ebx + ecx*4 + offset] A szögletes zárójelben lévő számtani kifejezések; továbbá méretkulcsszavakat, például bájtot (byte), szót (word), vagy dupla szót (dword) kell használni, ha a méret nem határozható meg az operandusokból.

Sok x86 assembler Intel szintaxist használ, beleértve a FASM, MASM, NASM, TASM és YASM szintaxist. A GNU assembler, amely eredetileg AT&T szintaxist használt, a 2.10-es verzió óta mindkét szintaxist támogatja a .intel_syntax direktíván keresztül. Az AT&T x86 szintaxisának egy furcsasága, hogy az x87 lebegőpontos operandusok felcserélődnek, ami egy az eredeti AT&T assemblerből örökölt hiba.

Az AT&T szintaxisa szinte univerzális más architektúrákban (megtartva a mov utasítás operandussorrendjét); eredetileg PDP-11 assemblyhez tervezték, és átkerült a Unix-szerű rendszerekre. Ezzel szemben az Intel szintaxisa az x86 architektúrára jellemző, és az x86 platform hivatalos dokumentációjában is ezt használják. Az x86 architektúrát megelőző Intel 8080 szintén a "destination-first" („a cél az első”) sorrendet használja a mov utasításokhoz.

Foglalt szavak A legtöbb x86-os assembly nyelvben a foglalt szavak két részből állnak: mnemonikokból, amelyek opkódokká fordíthatók le, és direktívákból (vagy „pszeudo-opokból”), amelyek az assembler program olyan funkcióihoz férnek hozzá, amelyek túlmutatnak az opkódok egyszerű fordításán. Az előbbi rész listáját lásd az x86 utasításlistákban. Az utóbbi rész nagymértékben assembly-függő, az Intel-szintaxisú assemblerek között nincs szabvány. Az AT&T-szintaxisú assemblerek közös elnevezési módot használnak a direktívák esetében (minden direktíva ponttal kezdődik, mint például az .ascii), és számos alapvető direktíva, például az .ascii és a .string széles körben támogatott.

Regiszterek Az x86 processzorok olyan regiszterkészlettel rendelkeznek, amelyek bináris adatok és címek tárolására szolgálnak a program végrehajtása során. Ezeket a regisztereket általános célú regiszterekre, szegmensregiszterekre, utasításmutatóra, FLAGS regiszterre és a későbbi processzormodellekben bevezetett különféle bővítőregiszterekre osztják. Minden regiszter az általános képességein túl specifikus funkciókkal is rendelkezik:

Általános célú regiszterek Ezeknek a regisztereknek hagyományos szerepük van, de használatuk nincs szigorúan kikényszerítve. A programok általában szabadon használhatják őket más célokra.

AX (Accumulator register): Elsősorban aritmetikai, logikai és adatátviteli műveletekben használják. Előnyben részesítik a szorzást és osztást végző utasítások, valamint a karakterlánc-betöltési és -tárolási műveletek. Az AX-szel történő azonnali ALU műveletek és cserék kompaktabban kódolhatók. BX (Base register): Alapmutató a memória-hozzáféréshez. Adatszerkezetek báziscímét tárolhatja, és hasznos indexelt címzési módokban. XLAT-tal együtt használják. CX (Count register): Számlálóként szolgál ciklus-, karakterlánc- és eltolás/forgatás utasításokban. Az iteratív műveletek gyakran használják a CX-et annak meghatározására, hogy egy ciklust vagy műveletet hányszor kell végrehajtani. DX (Data register): Az AX-szel együtt használják olyan szorzási és osztási műveletekhez, amelyek 16 bitnél nagyobb eredményeket hoznak. I/O portcímeket is tárol az IN és OUT utasításokhoz. SP (Stack pointer): A memória veremének tetejére mutat. Automatikusan frissül PUSH és POP műveletek során. BP (Base Pointer): A hívásverem tetejére mutat. Elsősorban függvényparaméterek és a hívásvermen belüli lokális változók elérésére használják. SI (Source Index): Pointerként szolgál a forrásra karakterlánc- és memóriatömb-műveletekben. Az olyan utasítások, mint a MOVS (move string), az SI-t használják adatok memóriából való olvasására. A BX-hez hasonlóan indexelésre is használható. Hozzáadható a BP-hez vagy a BX-hez dupla indexeléshez. DI (Destination Index): Pointerként szolgál a célra karakterlánc- és memóriatömb-műveletekben. Az SI-vel együtt működik az adatokat másoló vagy összehasonlító utasításokban, az eredményeket memóriába írva. A BX-hez hasonlóan indexelésre is használható. Hozzáadható a BP-hez vagy a BX-hez dupla indexeléshez.

Az általános regiszterek mellett léteznek még a következők is: Instruction Pointer (IP): A kódszegmensen (CS) belül végrehajtandó következő utasítás eltolási címét tartalmazza. A következő utasítás első bájtjára mutat. Bár az IP regisztert a programozók nem tudják közvetlenül olvasni, az értéke a vezérlési folyamat utasításai, például az ugrások, hívások és megszakítások révén változik, amelyek megváltoztatják a végrehajtás folyamatát. FLAGS regiszter: Állapot-, vezérlő- és rendszerjelzők halmazát tartalmazza, amelyek a műveletek eredményét tükrözik és a processzor működését vezérlik. Szegmensregiszterek (CS, DS, ES, SS): Meghatározzák, hol kezdődik egy 64k-os szegmens (az FS és GS regiszterek a 80386-os és újabb verziókban kerültek hozzáadásra) Extra bővítőregiszterek (MMX, 3DNow!, SSE stb.) (csak Pentium és újabb rendszereken).

Az x86 regisztereket a legtöbb utasítás használhatja. Például az Intel szintaxisában: mov ax, 1234h ; copies the value 1234hex (4660d) into register AX mov bx, ax ; copies the value of the AX register into the BX register

Szegmentált címzés Az x86 architektúra valós és virtuális 8086 módban a szegmentálás néven ismert folyamatot használja a memória címzésére, nem pedig a sok más környezetben használt flat memória modellt. A szegmentálás egy memóriacím két részből, egy szegmensből (segment) és egy ofszetből (offset) való összeállítását jelenti; a szegmens egy 64 KiB (64×210) méretű címcsoport elejére mutat, és az ofszet határozza meg, hogy a kívánt cím milyen messze van ettől a kezdőcímtől. Szegmentált címzés esetén két regiszterre van szükség egy teljes memóriacímhez. Az egyik a szegmens, a másik az ofszet tárolására szolgál. Ahhoz, hogy vissza lehessen alakítani egy flat címre, a szegmens értékét négy bittel balra eltolják (egyenértékű a 24-el vagy 16-tal való szorzással), majd hozzáadják az ofszethez, így létrehozva a teljes címet, ami lehetővé teszi a 64k-os korlát áttörését az okos címválasztás révén, bár ez jelentősen bonyolultabbá teszi a programozást.

Valós módban, például, ha a DS a hexadecimális számot, a DX pedig a számot tartalmazza, akkor együttesen a 0xDEAD * 0x10 + 0xCAFE == 0xEB5CE memóriacímre mutatnának. Ezért a CPU valós módban akár bájtot (1 MiB) tud megcímezni. A szegmens- és ofszetértékek kombinálásával egy 20-bites címet kapunk.

Az eredeti IBM PC 640 KB-ra korlátozta a programokat, de egy kibővített memóriaspecifikációval (Expanded memory) egy bankváltási sémát (Bank switching) valósítottak meg, amely használaton kívül esett, amikor a későbbi operációs rendszerek, például a Windows, az újabb processzorok nagyobb címtartományait használták, és saját virtuális memória-sémákat vezettek be.

A védett módot, az Intel 80286-tal kezdődően, az OS/2 is kihasználta. Számos hiányosság, például a BIOS elérésének lehetetlensége és a processzor újraindítása nélküli valós módba való visszatérés lehetetlensége megakadályozta a széles körű elterjedését. A 80286 továbbra is a memória 16-bites szegmensekben történő címzésére korlátozódott, ami azt jelentette, hogy egyszerre csak 216 bájt (64 kilobájt) érhető el. A 80286 kibővített funkcióinak eléréséhez az operációs rendszer védett módba állította a processzort, lehetővé téve a 24-bites címzést, és így 224 bájt (16 megabájt) memóriát.

Védett módban a szegmensválasztó három részre bontható: egy 13 bites indexre, egy táblajelző bitre (Table Indicator), amely meghatározza, hogy a bejegyzés a GDT-ben vagy az LDT-ben van-e, és egy 2 bites kért jogosultsági szintre (Requested Privilege Level); lásd x86 memória szegmentálás.

Amikor egy szegmenssel és eltolással rendelkező címre hivatkozunk, a segment:offset jelölést használjuk, így a fenti példában a 0xEB5CE flat address felírható 0xDEAD:0xCAFE-ként vagy szegmens és offszet regiszter párként; DS:DX.

A szegmens regiszterek és az általános regiszterek néhány speciális kombinációja fontos címekre mutat: CS:IP (CS a kódszegmens, IP az utasításmutató) arra a címre mutat, ahonnan a processzor a következő kódbájtot fogja lekérni. SS:SP (SS a veremszegmens, SP a veremmutató) a verem tetejének címére, azaz a legutóbb beillesztett bájtra mutat.