X86 assembly

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(wd) 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(wd) 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(wd) utasítás a 0x90 opkóddá, a HLT(wd) 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(wd) A konstans értékek(wd) 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(wd)
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(wd), MASM(wd), NASM(wd), TASM(wd) és YASM szintaxist. A GNU assembler(wd), 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(wd), 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(wd) és virtuális 8086 módban(wd) 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(wd). 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(wd), például, ha a DS a 0xDEAD hexadecimális számot, a DX pedig a 0xCAFE 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 1 048 576 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(wd)) egy bankváltási sémát (Bank switching(wd)) 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(wd), 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(wd)) é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(wd)) 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(wd) vagy az LDT-ben(wd) van-e, és egy 2 bites kért jogosultsági szintre (Requested Privilege Level); lásd x86 memória szegmentálás(wd).

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:

Az Intel 80386 három működési móddal rendelkezett: valós móddal, védett móddal és virtuális móddal. A 80286-ban debütált védett módot kibővítették, lehetővé téve a 80386 számára akár 4 GB memória címzését is. A vadonatúj virtuális 8086 mód (VM86) lehetővé tette egy vagy több valós módú program futtatását védett környezetben, amely nagyrészt emulálta a valós módot, bár egyes programok nem voltak kompatibilisek (jellemzően memória-címzési trükkök vagy meghatározatlan op-kódok használata miatt).

A 80386 kiterjesztett védett módjának 32 bites lapos memória modellje(wd) lehet a legfontosabb funkcióváltozás az x86 processzorcsaládban, amíg az AMD 2003-ban ki nem adta az x86-64-et, mivel ez segítette a Windows 3.1 (amely a védett módra támaszkodott) nagymértékű elterjedését, mivel a Windows mostantól sok alkalmazást, beleértve a DOS-alkalmazásokat is, futtathatott egyszerre a virtuális memória és az egyszerű multitasking használatával.

Végrehajtási módok

Az x86 processzorok ötféle működési módot támogatnak az x86 kódhoz: valós módot (Real Mode), védett módot (Protected Mode), hosszú módot (Long Mode), virtuális 86 módot (Virtual 86 Mode) és rendszerfelügyeleti módot (System Management Mode), amelyekben egyes utasítások elérhetők, mások nem. A 16 bites x86 processzorokon, amelyek a 8086, 8088, 80186, 80188 és 80286, az utasítások egy 16 bites részhalmaza érhető el. Ezek az utasítások valós módban minden x86 processzoron elérhetők, és 16 bites védett módban (80286-tól kezdődően) további, a védett módhoz kapcsolódó utasítások érhetők el. A 80386-os és újabb verziókon a 32 bites utasítások (beleértve a későbbi kiterjesztéseket is) szintén elérhetők minden módban, beleértve a valós módot is; ezeken a CPU-kon V86 mód és 32 bites védett mód is elérhető, további utasításokkal ezekben a módokban a funkcióik kezeléséhez. Az SMM, néhány saját speciális utasítással, néhány Intel i386SL, i486 és újabb CPU-n érhető el. Végül, hosszú módban (az AMD Opterontól felfelé) 64 bites utasítások és több regiszter is elérhető. Az utasításkészlet minden módban hasonló, de a memóriacímzés és a szóméret változó, ami eltérő programozási stratégiákat igényel.

Az x86 kód futtatásának módjai a következők:

Módok váltása

A processzor bekapcsolás után azonnal valós módban fut, így egy operációs rendszer kernelének vagy más programnak explicit módon át kell váltania egy másik módba, ha nem valós módban kíván futni. Az üzemmódok közötti váltás a processzor vezérlőregisztereinek(wd) bizonyos bitjeinek módosításával történik némi előkészítés után, és a váltás után további beállításokra lehet szükség.

Példák

Egy régebbi BIOS-t futtató számítógépen a BIOS és a rendszerbetöltő valós módban fut. A 64 bites operációs rendszer kernelje ellenőrzi és hosszú módba kapcsolja a CPU-t, majd új kernel módú(wd) szálakat indít 64 bites kód futtatásával.

Egy UEFI-t futtató számítógépen az UEFI firmware (a CSM és a régebbi Option ROM(wd) kivételével), az UEFI rendszerbetöltő és az UEFI operációs rendszer kernelje is hosszú módban(wd) fut.

Utasítástípusok

Általánosságban elmondható, hogy a modern x86 utasításkészlet jellemzői a következők:

Stack utasítások

Az x86 architektúra hardveresen támogatja a hívási veremmechanizmust (call stack). Az olyan utasítások, mint a push, pop, call és ret, a megfelelően beállított veremmel együtt használatosak paraméterek átadására, a helyi adatok számára helyfoglalásra, valamint a hívás-visszatérési pontok mentésére és visszaállítására. A ret size utasítás nagyon hasznos a helytakarékos (és gyors) hívási konvenciók(wd) megvalósításához, ahol a hívott fél felelős a paraméterek által elfoglalt veremterület visszanyeréséért.

Amikor egy veremkeretet állítunk be egy rekurzív eljárás lokális adatainak tárolására, számos lehetőség közül választhatunk; a magas szintű enter utasítás (a 80186-ossal bevezetve) egy eljárás-beágyazási mélység argumentumot, valamint egy lokális méret argumentumot is fogad el, és gyorsabb lehet, mint a regiszterek explicitebb manipulálása (például push bp ; mov bp, sp ; sub sp, size). Az, hogy gyorsabb vagy lassabb, az adott x86-processzoros implementációtól, valamint a fordító, a programozó vagy az adott programkód által használt hívási konvenciótól függ; a legtöbb x86-os kódot különböző gyártók x86-processzorain és a processzorok különböző technológiai generációin való futtatásra tervezték, ami nagyon eltérő mikroarchitektúrákat és mikrokód-megoldásokat(wd), valamint változó kapu- és tranzisztorszintű tervezési döntéseket von maga után.

A címzési módok teljes skálája (beleértve az „azonnali”, immediate és az „alap+eltolás”, base+offset módokat is), még olyan utasítások esetén is, mint a push és a pop, egyszerűvé teszi a verem közvetlen használatát az integer, lebegőpontos és cím(wd) adatokhoz, valamint az ABI specifikációit és mechanizmusait viszonylag egyszerűvé teszi egyes RISC architektúrákhoz képest (amelyek explicitebb call stack részleteket igényelnek).

Egész ALU utasítások

Az x86 assembly tartalmazza a standard matematikai műveleteket: összeadás, add, sub, neg, imul és idiv (előjeles egész számokhoz), mul és div (előjel nélküli egész számokhoz); az and, or, xor és not logikai operátorokat; a biteltolásos aritmetikai és logikai műveleteket, sal/sar (előjeles egész számokhoz), shl/shr (előjel nélküli egész számokhoz); forgatást átvitellel és anélkül, rcl/rcr, rol/ror, valamint a BCD aritmetikai utasítások komplementerét, aaa, aad, daa. Az olyan utasítások, mint a cmp és a test(wd), az operandusok módosítása nélkül állítják be a jelzőket.

Lebegőpontos utasítások

Az x86 assembly nyelv utasításokat tartalmaz egy veremalapú lebegőpontos egységhez (FPU). Az FPU egy opcionális, különálló társprocesszor volt a 8086-tól a 80386-ig, chipre integrált opció volt a 80486 sorozathoz, és a Pentiummal kezdve minden Intel x86 CPU-ban szabványos funkció a 80486 óta. Az FPU utasításai közé tartozik az összeadás, kivonás, tagadás, szorzás, osztás, maradék, négyzetgyökvonás, egész csonkolás, tört csonkolás és a kettő hatványán való skálázás. A műveletek konverziós utasításokat is tartalmaznak, amelyek a következő formátumok bármelyikében képesek betölteni vagy tárolni egy értéket a memóriából: bináris kódolású decimális, 32 bites egész, 64 bites egész, 32 bites lebegőpontos, 64 bites lebegőpontos vagy 80 bites lebegőpontos (betöltéskor az érték az aktuálisan használt lebegőpontos módba konvertálódik). Az x86 számos transzcendens függvényt(wd) is tartalmaz, beleértve a szinusz, koszinusz, tangens, arkusztangens függvényt, a 2-es alapú hatványozást és a 2-es, 10-es vagy e-alapú logaritmusokat.

Az utasítások veremregiszterről veremregiszterre való formátuma általában fop st, st(n) vagy fop st(n), st, ahol st egyenértékű az st(0)-val, és st(n) a 8 veremregiszter egyike (st(0), st(1), ..., st(7)). Az egész számokhoz hasonlóan az első operandus egyben az első forrásoperandus és a céloperandus is. Az fsubr és az fdivr utasításokat kell kiemelni, mivel ezek cserélik fel először a forrásoperandusokat a kivonás vagy osztás végrehajtása előtt. Az összeadás, kivonás, szorzás, osztás, tárolás és összehasonlítás utasítások olyan utasításmódokat tartalmaznak, amelyek a műveletük befejezése után a verem tetejét veszik fel. Tehát például az faddp st(1), st végrehajtja az st(1) = st(1) + st(0) számítást, majd eltávolítja az st(0)-t a verem tetejéről, így az st(1)-ben kapott eredmény az st(0) verem tetejére kerül.

SIMD utasítások

A modern x86-os CPU-k SIMD utasításokat tartalmaznak, amelyek nagyrészt ugyanazt a műveletet hajtják végre párhuzamosan számos, széles SIMD regiszterben kódolt értékkel. Különböző utasítástechnológiák támogatják a különböző műveleteket különböző regiszterkészleteken, de teljes egészében (az MMX-től az SSE4.2-ig) általános számításokat tartalmaznak egész vagy lebegőpontos aritmetikában (összeadás, kivonás, szorzás, eltolás, minimalizálás, maximalizálás, összehasonlítás, osztás vagy négyzetgyökvonás). Tehát például a paddw mm0, mm1 4 párhuzamos 16 bites (a w jelzi) egész értéket összead (a padd jelzi) mm0 értékekből mm1-hez, és az eredményt mm0-ban tárolja. A Streaming SIMD Extensions vagy SSE tartalmaz egy lebegőpontos módot is, amelyben a regiszterek legelső értékének módosítása történik meg (az SSE2-ben kibővítve). Néhány más szokatlan utasítást is hozzáadtak, beleértve az abszolút különbségek összegzését(wd) (a videotömörítés során a mozgásbecsléshez(wd) használják, például az MPEG-ben) és egy 16 bites szorzóösszesítő utasítást (hasznos szoftveralapú alfa-keveréshez és digitális szűréshez(wd)). Az SSE (az SSE3 óta) és a 3DNow! kiterjesztések összeadási és kivonási utasításokat tartalmaznak a párosított lebegőpontos értékek komplex számokhoz hasonlóan történő kezelésére.

Ezek az utasításkészletek számos fix részszavas utasítást is tartalmaznak az értékek regisztereken belüli keveréséhez, beszúrásához és kinyeréséhez. Ezenkívül vannak utasítások az adatok egész regiszterek és az XMM (SSE-ben használt)/FPU (MMX-ben használt) regiszterek közötti mozgatásához.

Memória utasítások

Az x86 processzor komplex címzési módokat is tartalmaz a memória azonnali eltolással történő címzéséhez, egy regiszterhez, egy eltolt regiszterhez, egy skálázott regiszterhez eltolással vagy anélkül, valamint egy opcionális eltolt regiszterhez és egy további skálázott regiszterhez. Így például a mov eax, [Table + ebx + esi*4] egyetlen utasításként kódolható, amely 32 bit adatot tölt be a ds szelektorból (Table + ebx + esi * 4) eltolásként kiszámított címről, és az eax regiszterbe tárolja. Általánosságban elmondható, hogy az x86 processzorok a működésük alatt álló regiszter méretéhez igazított memóriát tudnak betölteni és használni. (A SIMD utasítások félig betöltött utasításokat is tartalmaznak.)

A legtöbb 2 operandusú x86 utasítás, beleértve az egészértékű ALU utasításokat is, egy szabványos "címzési mód(wd) bájtot" használ, amelyet gyakran MOD-REG-R/M bájtnak(wd) neveznek. Sok 32 bites x86 utasításnak van egy SIB címzési mód bájtja(wd) is, amely a MOD-REG-R/M bájtot követi.

Elvileg, mivel az utasítás opkódja elkülönül a címzési mód bájtjától, ezek az utasítások ortogonálisak, mivel bármelyik opkód keverhető és illeszthető bármilyen címzési móddal. Az x86 utasításkészletet azonban általában nem ortogonálisnak tekintik, mivel a legtöbb diadikus művelet(wd) nem képes memóriáról memóriára működni, más opkódoknak van valamilyen rögzített címzési módjuk (nincs címzési mód bájtjuk), és minden regiszternek van egy preferált felhasználása.

Az x86 utasításkészlet karakterlánc betöltési, tárolási, mozgatási, szkennelési és összehasonlítási utasításokat (lods, stos, movs, scas és cmps) tartalmaz, amelyek minden műveletet egy megadott méretig hajtanak végre (b a 8 bites bájthoz, w a 16 bites szóhoz, d a 32 bites dupla szóhoz), majd növelik/csökkentik (a DF-től, az irányjelzőtől függően) az implicit címregisztert (si a lods-hoz, di a stos és scas-hoz, és mindkettő a movs és cmps-hez). A betöltési, tárolási és szkennelési műveletekhez az implicit cél/forrás/összehasonlító regiszter az al, ax vagy eax regiszterben található (a mérettől függően). A használt implicit szegmensregiszterek a ds az si-hez és az es a di-hez. A cx vagy ecx regisztert csökkenő számlálóként használják, és a művelet leáll, amikor a számláló eléri a nullát, vagy szkennelések és összehasonlítások esetén, amikor egyenlőséget vagy egyenlőtlenséget észlelnek. Sajnos az évek során ezen utasítások némelyikének teljesítménye elhanyagolódott, és bizonyos esetekben gyorsabb eredményeket lehet elérni elemibb utasításokkal történő kódolással. Az Intel és az AMD azonban frissített néhány utasítást, és 2025-től néhányuk nagyon tiszteletre méltó teljesítményt nyújt.

A verem egy memóriaterület és egy hozzá tartozó veremmutató (stack pointer), amely a verembe utoljára helyezett elemre mutat. A veremmutató értéke csökken az elemek hozzáadása előtt, push, és növekszik az elemek eltávolítása után, pop. 16 bites módban ez az implicit veremmutató SS:[SP], 32 bites módban SS:[ESP], 64 bites módban pedig [RSP] címmel rendelkezik. A veremmutató az utoljára tárolt értékre mutat, feltételezve, hogy a mérete megegyezik a processzor működési módjával (azaz 16, 32 vagy 64 bit), hogy megfeleljen a push/pop/call/ret utasítások alapértelmezett szélességének. Ide tartoznak az enter és leave utasítások is, amelyek adatokat foglalnak le és távolítanak el a verem tetejéről, miközben egy veremkeret-mutatót állítanak be a bp/ebp/rbp regiszterben. Azonban a közvetlen beállítás, vagyis az sp/esp/rsp regiszterhez való hozzáadás és kivonás is támogatott, így az enter/leave utasítások általában szükségtelenek.

Ez a kód egy tipikus magas szintű nyelvi függvény kezdete, amikor a fordítási optimalizálás ki van kapcsolva a hibakeresés megkönnyítése érdekében:

 push    rbp       ; Save the calling function’s stack frame pointer (rbp register)
 mov     rbp, rsp  ; Make a new stack frame below our caller’s stack
 sub     rsp, 32   ; Reserve 32 bytes of stack space for this function’s local variables.
                   ; Local variables will be below rbp and can be referenced relative to rbp,
                   ; again best for ease of debugging, but for best performance rbp will not
                   ; be used at all, and local variables would be referenced relative to rsp
                   ; because, apart from the code saving, rbp then is free for other uses.
  …       …        ; However, if rbp is altered here, its value should be preserved for the caller.
 mov [rbp-8], rdx  ; Example of writing to a local variable (by its memory location) from register rdx

...funkcionálisan egyenértékű egyszerűen a következővel:

 enter   32, 0

A verem manipulálására szolgáló egyéb utasítások közé tartozik a pushfd(32 bites) / pushfq(64 bites) és a popfd/popfq az EFLAGS (32 bites) / RFLAGS (64 bites) regiszter tárolásához és lekéréséhez.

A SIMD betöltés vagy tárolás értékei feltételezhetően a SIMD regiszter szomszédos pozícióiba vannak csomagolva, és szekvenciális little-endian sorrendben igazítják őket. Egyes SSE betöltési és tárolási utasítások megfelelő működéséhez 16 bájtos igazítás szükséges. A SIMD utasításkészletek tartalmaznak "prefetch" utasításokat is, amelyek végrehajtják a betöltést, de nem céloznak meg egyetlen regisztert sem, amelyeket a gyorsítótár betöltéséhez használnak. Az SSE utasításkészletek tartalmaznak nem időbeli tárolási utasításokat is, amelyek közvetlenül a memóriába mentik a mentéseket gyorsítótár-lefoglalás nélkül, ha a cél még nincs gyorsítótárazva (különben úgy fog viselkedni, mint egy normál tárolás).

A legtöbb általános egész és lebegőpontos (de nem SIMD) utasítás egy paramétert használhat komplex címként második forrásparaméterként. Az egész utasítások egy memóriaparamétert is elfogadhatnak céloperandusként.

Programfolyamat

Az x86 assembly rendelkezik egy feltétel nélküli ugrás művelettel, a jmp-vel, amely paraméterként azonnali címet, regisztert vagy indirekt címet vehet fel (vegye figyelembe, hogy a legtöbb RISC processzor csak linkregisztert(wd) vagy rövid azonnali elmozdulást támogat az ugráshoz).

Számos feltételes ugrás is támogatott, többek között a jz (jump on zero), jnz (jump on non-zero), jg (jump on greater than, signed), jl (jump on less than, signed), ja (jump on above/greater than, unsigned), jb (jump on below/less than, unsigned)), ja (ugrás felette/nagyobb, mint, előjel nélküli), jb (ugrás alatta/kisebb, mint, előjel nélküli). Ezek a feltételes műveletek az (E)FLAGS regiszterben lévő adott bitek állapotán alapulnak. Számos aritmetikai és logikai művelet állítja be, törli vagy kiegészíti ezeket a jelzőket az eredményüktől függően. Az összehasonlító cmp (compare) és test utasítások úgy állítják be a jelzőket, mintha kivonást, illetve bitenkénti ÉS műveletet végeztek volna, az operandusok értékének megváltoztatása nélkül. Léteznek olyan utasítások is, mint a clc (clear carry flag) és a cmc (complement carry flag), amelyek közvetlenül a jelzőkre hatnak. A lebegőpontos összehasonlításokat fcom vagy ficom utasításokon keresztül hajtják végre, amelyeket végül egészértékű jelzőkké kell konvertálni.

Minden ugrási műveletnek három különböző formája van, az operandus méretétől függően. A rövid ugrás (short jump) egy 8 bites előjeles operandust használ, amely relatív eltolás az aktuális utasításhoz képest. A közeli ugrás (near jump) hasonló a rövid ugráshoz, de 16 bites előjeles operandust használ (valós vagy védett módban) vagy 32 bites előjeles operandust (csak 32 bites védett módban). A távoli ugrás (far jump) az, amely a teljes szegmens bázis:eltolás értékét használja abszolút címként. Mindegyiknek létezik közvetett és indexelt formája is.

Az egyszerű ugrási műveleteken kívül léteznek a call (call a subroutine) és a ret (return from subroutine) utasítások. Mielőtt a vezérlés átadásra kerülne az alprogramnak, a call a call-ot követő utasítás eltolás címét a veremtárba helyezi; a ret ezt az értéket kiveszi a veremtárból, és ráugrik, gyakorlatilag visszaállítva a vezérlés folyamatát a programnak erre a részére. far call esetén a szegmens alapja kerül beírásra, majd az eltolás következik; a far ret kiveszi az eltolást, majd a szegmens alapját a visszatéréshez.

Két hasonló utasítás is létezik, az int(wd) (interrupt), amely elmenti az aktuális (E)FLAGS regiszter értékét a veremre, majd egy távoli hívást (far call) hajt végre, azzal a különbséggel, hogy cím helyett egy megszakításvektort (nterrupt vector), azaz a megszakításkezelők címeinek táblázatába mutató indexet használ. A megszakításkezelő jellemzően az összes többi általa használt CPU regisztert elmenti, kivéve, ha azokat egy művelet eredményének a hívó programnak való visszaadására használják (szoftverben ezt interruptnak nevezik). A megszakításutasítástól visszaérkező egyező érték az iret, amely a visszatérés után visszaállítja a jelzőket. A fent leírt típusú lágy megszakításokat (soft interrupt) egyes operációs rendszerek rendszerhívásokhoz(wd) használják, és kemény megszakításkezelők hibakereséséhez is használhatók. A kemény megszakításokat (hard interrupt) külső hardveresemények váltják ki, és meg kell őrizniük az összes regiszterértéket, mivel az aktuálisan futó program állapota ismeretlen. Védett módban az operációs rendszer beállíthatja a megszakításokat egy feladatváltás kiváltására, amely automatikusan menti az aktív feladat összes regiszterét.

Példák

A következő példák az úgynevezett Intel-szintaxis-ízt (Intel-syntax flavor) használják, ahogyan azt a Microsoft MASM, NASM és sok más assembler is használja. (Megjegyzés: Létezik egy alternatív AT&T-syntax flavor is, ahol a forrás- és céloperandusok sorrendje felcserélődik, sok más különbség mellett.)

"Hello, world!" program operációs rendszer nélkül

A "Hello, world!" szöveg kiírható kevés operációs rendszer-segítséggel. A „Call outchr” egy olyan mechanizmust hív meg, amely egy AL-ben lévő karaktert ír ki a konzolra. A nullától eltérő hosszúságú karakterláncokat nulla bájttal kell lezárni. Megjegyzendő, hogy ez a példa egy 16 bites Intel 8086-ra vonatkozik.

hello:            
   mov    si,msg      ; address of string into SI
   cld              ; Clear direction to increment SI
   lodsb            ; Load first char in AL, inc SI 
chrlp:
   call outchr      ; Print character in AL
   lodsb            ; Load next character in AL, inc SI 
   or al, al        ; Is it a zero terminator?
   jnz chrlp        ; If not, continue
   ret              ; Return to caller

msg: db 'Hello, world!', 0xa, 0x0  ; string to be printed

"Hello, world!" program MS-DOS-hoz MASM stílusú assemblyben

A szoftveres megszakítás 21h utasítás használata az MS-DOS operációs rendszer meghívásához a kijelzőre történő kimenethez – más példák a libc C printf() rutinját használják a stdout-ra (szabványos kimenet) íráshoz. Megjegyzendő, hogy ez a példa 16 bites módot használ, mint egy Intel 8086. A következő példa egy Intel 386 kód 32 bites módban. A modern kód 64 bites módban lesz.

.model small
.stack 100h

.data
msg    db    'Hello world!$'

.code
start:
    mov    ah, 09h    ; Sets 8-bit register ‘ah’, the high byte of register ax, to 9, to
                   ; select a sub-function number of an MS-DOS routine called below
                   ; via the software interrupt int 21h to display a message
    lea    dx, msg    ; Takes the address of msg, stores the address in 16-bit register dx
    int    21h        ; Various MS-DOS routines are callable by the software interrupt 21h
                   ; Our required sub-function was set in register ah above

    mov    ax, 4C00h  ; Sets register ax to the sub-function number for MS-DOS’s software
                   ; interrupt int 21h for the service ‘terminate program’.
    int    21h        ; Calling this MS-DOS service never returns, as it ends the program.

end start

"Hello, world!" program Windowshoz MASM és NASM stílusú assemblyben

! MASMNASMLeírás
; requires /coff switch on 6.15 and earlier versions
.386
.model small,c
.stack 1000h
; Image base = 0x00400000
%define RVA(x) (x-0x00400000)
Preambulum. A MASM megköveteli a címmodell és a veremméret definiálását.
.data
msg     db "Hello world!",0
section .data
msg db "Hello world!"
Adatok szakasz. A db (define byte) pszeudoopciót használjuk egy karakterlánc definiálására.
.code
includelib libcmt.lib
includelib libvcruntime.lib
includelib libucrt.lib
includelib legacy_stdio_definitions.lib

extrn printf:near
extrn exit:near

public main
main proc
        push    offset msg
        call    printf
        push    0
        call    exit
main endp
end
section .text
push dword msg
call dword [printf]
push byte +0
call dword [exit]
ret

section .idata
dd RVA(msvcrt_LookupTable)
dd -1
dd 0
dd RVA(msvcrt_string)
dd RVA(msvcrt_imports)
times 5 dd 0 ; ends the descriptor table

msvcrt_string dd "msvcrt.dll", 0
msvcrt_LookupTable:
dd RVA(msvcrt_printf)
dd RVA(msvcrt_exit)
dd 0

msvcrt_imports:
printf dd RVA(msvcrt_printf)
exit dd RVA(msvcrt_exit)
dd 0

msvcrt_printf:
dw 1
dw "printf", 0

msvcrt_exit:
dw 2
dw "exit", 0
dd 0
A kód (.text szakasz) és az import tábla. A NASM-ben az import tábla manuálisan jön létre, míg a MASM példában direktívák használatosak a folyamat egyszerűsítése érdekében.

"Hello, world!" program Linuxhoz AT&T és NASM stílusú assemblyben

AT&T (GNU as)Intel (NASM)Leírás
.data
section .data
A Windows példához hasonlóan a .data fájl az inicializált adatok szakasza.
str: .ascii "Hello, world!\n"
str:     db 'Hello world!', 0Ah
Definiálj egy szöveges karakterláncot, amely tartalmazza a "Hello, world!" szöveget, majd egy új sort (\n, ami 0x0A). Kösd az "str" címkét a definiált karakterlánc címéhez.
str_len = . - str
str_len: equ $ - str
Számítsd ki az str karakterlánc hosszát. A . jelentése "here" a gas nyelvben, a $ pedig ugyanezt jelenti a nasm nyelvben. A "here"-ből az "str" kivonásával megkapjuk az előzőleg definiált karakterlánc hosszát.
.text
section .text
A Windows példához hasonlóan a .text a programkód szakasza.
.globl _start
global _start
exportáld a _start függvényt globális hatókörbe, hogy a linker "láthassa" azt
_start:
_start:
Definiáljunk egy _start nevű címkét, amelybe az alprogramunkat fogjuk írni. A _start név a Linux konvenció szerint a belépési pontot határozza meg.
    movl $4, %eax
    movl $1, %ebx
    movl $str, %ecx
    movl $str_len, %edx
    mov    eax, 4
    mov    ebx, 1
    mov    ecx, str
    mov    edx, str_len
Készítsen elő egy rendszerhívást. Az EAX=4 a "sys_write" hívást kéri Linux x86 rendszeren. Az EBX=1 a sys_write esetében a "stdout" értéket jelenti. Az ECX a írandó karakterláncot, az EDX pedig a írandó bájtok számát tartalmazza. Ez egyenértékű a libc-vel csomagolt write(1, str, str_len) verzióval.
    int $0x80
    int 80h
x86 rendszeren a "80h" rendszermegszakítást használják a rendszerhívás meghívására az eax, ebx, ecx és edx értékei alapján.
    movl $1, %eax
    movl $0, %ebx
    int $0x80
    mov    eax, 1
    mov    ebx, 0
    int    80h
Tölts be egy másik rendszerhívást, majd hívd meg INT 80h paranccsal: EAX=1 = sys_exit, és az EBX for sys_exit tartalmazza a visszatérési értéket. A 0 visszatérési érték normál kilépést jelent. C szintaxisban az _exit(0);.

Megjegyzés az NASM-hez:

; Ez a program 32 bites védett módban fut.
;  build: nasm -f elf -F stabs name.asm
;  link:  ld -o name name.o
;
; 64 bites long módban használhatsz 64 bites regisztereket (pl. rax az eax helyett, rbx az ebx helyett stb.)
; A build parancsban a "-f elf" paramétert "-f elf64"-re kell cserélni.
; 64 bites long módban a "lea rcx, str" lenne az üzenet címe, jegyezd meg a 64 bites rcx regisztert.

"Hello, world!" program Linuxhoz NASM stílusú assemblyben a C szabványos könyvtár használatával

;
;  This program runs in 32-bit protected mode.
;  gcc links the standard-C library by default

;  build: nasm -f elf -F stabs name.asm
;  link:  gcc -o name name.o
;
; In 64-bit long mode you can use 64-bit registers (e.g. rax instead of eax, rbx instead of ebx, etc..)
; Also change "-f elf " for "-f elf64" in build command.
;
        global  main                            ; ‘main’ must be defined, as it being compiled
                                                ; against the C Standard Library
        extern  printf                          ; declares the use of external symbol, as printf
                                                ; printf is declared in a different object-module.
                                                ; The linker resolves this symbol later.

segment .data                                   ; section for initialized data
    string db 'Hello world!', 0Ah, 0            ; message string ending with a newline char (10
                                                ; decimal) and the zero byte ‘NUL’ terminator
                                                ; ‘string’ now refers to the starting address
                                                ; at which 'Hello, World' is stored.

segment .text
main:
        push    string                          ; Push the address of ‘string’ onto the stack.
                                                ; This reduces esp by 4 bytes before storing
                                                ; the 4-byte address ‘string’ into memory at
                                                ; the new esp, the new bottom of the stack.
                                                ; This will be an argument to printf()

        call    printf                          ; calls the C printf() function.
        add     esp, 4                          ; Increases the stack-pointer by 4 to put it back
                                                ; to where it was before the ‘push’, which
                                                ; reduced it by 4 bytes.
        ret                                     ; Return to our caller.

Mivel a C futtatókörnyezetet használjuk, a main() függvényt a C futtatókörnyezet elvárásai szerint definiáljuk. Az exit meghívása helyett egyszerűen visszatérünk a main függvényből, hogy a futtatókörnyezet elvégezze a tisztítást.

"Hello, world!" program 64 bites módban futó Linuxhoz NASM stílusú assemblyben

Ez a példa modern 64 bites módban van.

;  build: nasm -f elf64 -F dwarf hello.asm
;  link:  ld -o hello hello.o

DEFAULT REL                ; use RIP-relative addressing modes by default, so [foo] = [rel foo]

SECTION .rodata            ; read-only data should go in the .rodata section on GNU/Linux, like .rdata on Windows
Hello:        db "Hello world!", 10   ; Ending with a byte 10 = newline (ASCII LF)
len_Hello:    equ $-Hello             ; Get NASM to calculate the length as an assembly-time constant
                                    ; the ‘$’ symbol means ‘here’. write() takes a length so that
                                    ; a zero-terminated C-style string isn't needed.
                                    ; It would be for C puts()

SECTION .text

global _start
_start:
    mov eax, 1                ; __NR_write syscall number from Linux asm/unistd_64.h (x86_64)
    mov edi, 1                ; int fd = STDOUT_FILENO
    lea rsi, [rel Hello]            ; x86-64 uses RIP-relative LEA to put static addresses into regs
    mov rdx, len_Hello        ; size_t count = len_Hello
    syscall                    ; write(1, Hello, len_Hello);  call into the kernel to actually do the system call
     ;; return value in RAX.  RCX and R11 are also overwritten by syscall

    mov eax, 60                ; __NR_exit call number (x86_64) is stored in register eax.
    xor edi, edi            ; This zeros edi and also rdi.
                            ; This xor-self trick is the preferred common idiom for zeroing
                            ; a register, and is always by far the fastest method.
                            ; When a 32-bit value is stored into eg edx, the high bits 63:32 are
                            ; automatically zeroed too in every case. This saves you having to set
                            ; the bits with an extra instruction, as this is a case very commonly
                            ; needed, for an entire 64-bit register to be filled with a 32-bit value.
                            ; This sets our routine’s exit status = 0 (exit normally)
    syscall                    ; _exit(0)

Az strace alatt futtatva ellenőrizhető, hogy a folyamat nem hajt végre extra rendszerhívásokat. A printf verzió sokkal több rendszerhívást kezdeményezne a libc inicializálásához és a dinamikus linkeléshez(wd). De ez egy statikus futtatható fájl, mivel az ld kapcsolóval linkeltünk -pie vagy megosztott könyvtárak nélkül; a felhasználói térben csak a megadott utasítások futnak.

$ strace ./hello > /dev/null                    # without a redirect, your program's stdout is mixed with strace's logging on stderr.  Which is normally fine
execve("./hello", ["./hello"], 0x7ffc8b0b3570 /* 51 vars */) = 0
write(1, "Hello world!\n", 13)          = 13
exit(0)                                 = ?
+++ exited with 0 +++

A flags regiszter használata

Az x86 architektúrában a flageket széles körben használják összehasonlításokhoz. Amikor két adat összehasonlításra kerül, a CPU beállítja a vonatkozó flaget vagy flageket. Ezt követően feltételes ugrási utasításokkal ellenőrizhetők a flagek, és elágazhatnak a futtatandó kódhoz, pl.:

    cmp    eax, ebx
    jne    do_something
    ; ...
do_something:
    ; do something here

Az összehasonlítási utasításokon kívül számos aritmetikai és egyéb utasítás létezik, amelyek biteket állítanak be a flags regiszterben. További példák a sub, test és add utasítások, és még sok más. Az olyan gyakori kombinációk, mint a cmp + feltételes ugrás, belsőleg "összeolvadnak" („makrofúzió”) egyetlen mikroutasítássá(wd) (μ-op), és gyorsak, feltéve, hogy a processzor kitalálja, merre fog menni a feltételes ugrás, jump vs. continue.

A flags regisztert az x86 architektúrában bizonyos funkciók vagy végrehajtási módok be- és kikapcsolására is használják. Például az összes maszkolható megszakítás letiltásához használhatja a következő utasítást:

    cli

A flags regiszter közvetlenül is elérhető. A flags regiszter alsó 8 bitje betölthető az ah-ba a lahf utasítással. A teljes flags regiszter a pushfd/pushfq, popfd/popfq, int (beleértve az into utasítást is) és iret utasításokkal is áthelyezhető a verembe és a veremből.

Az x87 lebegőpontos matematikai alrendszernek saját független „flags” típusú regisztere is van, az fp állapotszó. Az 1990-es években nehézkes és lassú eljárás volt a regiszterben található flag bitek elérése, de a modern processzorokon léteznek „két lebegőpontos érték összehasonlítása” utasítások, amelyek közvetlenül használhatók a normál feltételes ugrás/elágazás utasításokkal, bármilyen közbeeső lépés nélkül.

Az utasításmutató regiszter használata

Az utasításmutatót 16 bites módban ip-nek, 32 bites módban eip-nek, 64 bites módban pedig rip-nek nevezik. Az utasításmutató regiszter a processzor által végrehajtandó következő utasítás címére mutat. 16 bites vagy 32 bites módban nem érhető el közvetlenül, de az alábbihoz hasonló sorozat írható a next_line címének az eax regiszterbe való beillesztésére (32 bites kód):

    call    next_line
next_line:
    pop    eax

Az utasításmutatóba (programszámlálóba) írás egyszerű – egy jmp utasítás a megadott célcímet az utasításmutatóba tárolja, így például egy olyan sorozat, mint a következő, a rax tartalmát a rip-be (64 bites kód) helyezi:

    jmp    rax

64 bites módban az utasítások az utasításmutatóhoz képest relatív adatokra hivatkozhatnak, így kevésbé van szükség az utasításmutató értékének egy másik regiszterbe másolására.

Lásd még

Jegyzetek

  1. ↑ "Intel 8008 (i8008) microprocessor family". www.cpu-world.com. 2021. április 17. dátummal az eredetiből archiválva. Hozzáférés: 2021. március 25.
  2. ↑ "Intel 8008". CPU MUSEUM - MUSEUM OF MICROPROCESSORS & DIE PHOTOGRAPHY (amerikai angol nyelven). 2021. április 1. dátummal az eredetiből archiválva. Hozzáférés: 2021. március 25.
  3. 1 2 3 "8086 Opcode Map". Home of the Millilitre. 2026. január 23. dátummal az eredetiből archiválva. Hozzáférés: 2025. december 22.
  4. 1 2 3 4 5 Narayam, Ram (2007. október 17.). "Linux assemblers: A comparison of GAS and NASM". IBM. 2013. október 3. dátummal az eredeti címről archiválva. Hozzáférés: 2008. július 2.
  5. ↑ "The Creation of Unix". 2014. április 2. dátummal az eredeti címről archiválva.
  6. ↑ Hyde, Randall. "Which Assembler is the Best?". 2007. október 18. dátummal az eredeti címről archiválva. Hozzáférés: 2008. május 18.
  7. ↑ "GNU Assembler News, v2.1 supports Intel syntax". 2008. április 4. Hozzáférés: 2008. július 2.[halott link]
  8. ↑ "i386-Bugs (Using as)". Binutils documentation. 2020. január 15. dátummal az eredetiből archiválva. Hozzáférés: 2020. január 15.
  9. ↑ "Intel 8080 Assembly Language Programming Manual" (PDF). 2023. május 12. dátummal az eredeti (PDF) címről archiválva. Hozzáférés: 2023. május 12.
  10. ↑ "NASM - The Netwide Assembler". www.nasm.us. 2025. augusztus 14. dátummal az eredetiből archiválva. Hozzáférés: 2025. május 9.
  11. ↑ "Statements (Using as)". sourceware.org. 2025. május 10. dátummal az eredetiből archiválva. Hozzáférés: 2025. május 9.
  12. ↑ "Pseudo Ops (Using as) :: Assembler Directives". sourceware.org. 2025. május 17. dátummal az eredetiből archiválva. Hozzáférés: 2025. május 9.
  13. ↑ "Assembler Directives - x86 Assembly Language Reference Manual". docs.oracle.com. 2025. augusztus 14. dátummal az eredetiből archiválva. Hozzáférés: 2025. május 9.
  14. ↑ Mueller, Scott (2006. március 24.). "P2 (286) Second-Generation Processors". Upgrading and Repairing PCs, 17th Edition (Book) (17 ed.). Que. ISBN 0-7897-3404-4. Hozzáférés: 2017. december 6.
  15. ↑ Curtis Meadow. "Encoding of 8086 Instructions" Archiválva 2022. július 6-i dátummal a Wayback Machine-ben..
  16. ↑ Igor Kholodov. "6. Encoding x86 Instruction Operands, MOD-REG-R/M Byte" Archiválva 2022. március 31-i dátummal a Wayback Machine-ben..
  17. ↑ "Encoding x86 Instructions" Archiválva 2022. március 31-i dátummal a Wayback Machine-ben..
  18. ↑ Michael Abrash. "Zen of Assembly Language: Volume I, Knowledge". "Chapter 7: Memory Addressing". Section "mod-reg-rm Addressing" Archiválva 2022. március 4-i dátummal a Wayback Machine-ben..
  19. ↑ Intel 80386 Reference Programmer's Manual. "17.2.1 ModR/M and SIB Bytes" Archiválva 2021. december 20-i dátummal a Wayback Machine-ben.
  20. ↑ ""X86-64 Instruction Encoding: ModR/M and SIB bytes"". 2022. március 4. dátummal az eredetiből archiválva. Hozzáférés: 2022. március 4.
  21. ↑ "Figure 2-1. Intel 64 and IA-32 Architectures Instruction Format" Archiválva 2022. március 4-i dátummal a Wayback Machine-ben..
  22. ↑ "x86 Addressing Under the Hood" Archiválva 2022. március 4-i dátummal a Wayback Machine-ben..
  23. 1 2 Stephen McCamant. "Manual and Automated Binary Reverse Engineering" Archiválva 2022. március 4-i dátummal a Wayback Machine-ben..
  24. ↑ "X86 Instruction Wishlist" Archiválva 2022. március 4-i dátummal a Wayback Machine-ben..
  25. ↑ A linkregiszter (röviden LR) egy olyan regiszter, amely azt a címet tárolja, amelyre a program visszatér, amikor egy alprogramhívás befejeződik. Ez hatékonyabb, mint a hagyományosabb sémák, amelyek a visszatérési címeket egy hívási veremben, néha gépi veremben tárolják. A linkregiszter nem igényli a veremet tartalmazó memória írását és olvasását, ami jelentős százalékban megtakaríthatja a végrehajtási időt a kis alprogramok ismételt hívásaival.
  26. ↑ Peter Cordes (2011. december 18.). "NASM (Intel) versus AT&T Syntax: what are the advantages?". Stack Overflow. 2023. február 13. dátummal az eredetiből archiválva. Hozzáférés: 2023. február 13.
  27. ↑ "I just started Assembly". daniweb.com. 2008. 2019. január 29. dátummal az eredetiből archiválva. Hozzáférés: 2019. január 29.

Fordítás

További információk