bélyegkép|300px|Assembly kód és a belőle fordított gépi kód
Az assembly (angol: összerakás, összegyűjtés, összeépítés) a gépi kódhoz (a számítógép „anyanyelvéhez”) legközelebb álló, és így helykihasználás és futási idő szempontjából a leghatékonyabb általános célú programozási nyelv.
Habár az egyes architektúrák assembly nyelvei hasonlóak, mégis különböznek annyira, hogy az assembly kód ne legyen hordozható. Az assembly kódot az assembler fordító alakítja gépi kóddá.
Az assembly nyelv nem keverendő össze a gépi kóddal: egy assembly nyelvű program végrehajtható utasításai általában egy gépi kódú utasításnak felelnek meg, tehát az assembly egy programozási nyelv, a gépi kód az a tárgykód, amit csaknem minden programozási nyelv előállít végeredményként. Szimbolikus gépi kódnak is nevezik.
Az utasítások mellett még regisztereket, flageket és memóriacímeket is tartalmaz, egyes kódszakaszok címkézhetők az ugró utasítások számára. A legtöbb assemblyben kifejezések írhatók elnevezett konstansokkal és számokkal. Ezeket memóriacímzésre is fel lehet használni. Sok assembly további eszközöket tartalmaz a fejlesztés megkönnyítésére.
Commodore gépcsaládon vagy Sinclair gépeken három módszerrel lehetett programozni: interpretált BASIC – hátránya: a program futása lassú volt – továbbá gépi kód, illetve assembly. Nem létezett külön tárgykód, a program közvetlenül a memóriába lett fordítva. A gépek memóriamérete, illetve a háttértárak korlátozottsága nem is tette volna lehetővé C nyelv és standard C könyvtár használatát. A Commodore-16 16 KB memóriával rendelkezett, 32 KB ROM-ja – melynek kb. felét a BASIC-interpreter tette ki – tartalmazta az „operációs rendszerét”. Háttértárként egyoldalas floppyt – kétoldalas floppy használatához meg kellett fordítani a lemezt a meghajtóban – vagy magnókazettát használtak.
Régebben szélesebb körben használták, de hatékonysága miatt az assembly megmaradt az operációs rendszerek, illesztőprogramok és hasonlók programozásában.
Eredete Az első számítógépek programozása úgy történt, hogy a számításokat végző elemek huzalozását változtatták meg.
A számítógépeket a kezdetekben a processzorok utasításaihoz rendelt számok bevitelével (gépi kóddal) lehetett programozni, melyek ábrázolása eleinte bináris, majd később oktális (nyolcas) vagy hexadecimális (tizenhatos) számrendszerben (vagy röviden csak „hexában”) történt.
Az assembly kódhoz tartozó fordítóprogramot assemblernek nevezik. Ez készít a szöveges forrásprogramból egy olyan állományt, amely csaknem teljes egészében megfelel annak a memória képnek, amelyet a processzor végrehajtható programként értelmezni fog.
Az assembler "párja" a disassembler, ami a lefordított bináris kódot értelmezi és assembly forráskódú listává alakítja. Az alacsony szintű programozás további kellékei: memóriatartalom vizsgáló, "dump" program debugger – hibakereső program hexa editor – állományok hexadecimális (16-os számrendszerű), ha szükséges utasításszintű módosítását teszi lehetővé processzor szimulátor – adott processzorra írt programot "szoftveresen" futtat egy másik számítógépen, esetleg a célgép további hardver elemeit is emulálja
Terminológia A makró assembler (macro assembler) makróutasítást tartalmaz, ezzel elnevezhető egy programszakasz, ami a névvel hivatkozható más szakaszokból. A kereszt assembler (cross assembler) keresztfordítást végez, azaz nem arra a gépre készít futtatható programot, amin a program fordul. Az eredménykódot át kell másolni a célrendszerre. A pontos másolást a célrendszer számára csak olvasható média (ROM, EPROM, ...) vagy link segítségével. Ilyet nyújt például a Motorola S-record vagy az Intel HEX. Nemcsak kényelmi célokat szolgál, hanem így lehet programot készíteni olyan rendszerekre, ahol nem lehet fordítani, például beágyazott rendszerek. A magas szintű assembler (high-level assembler) strukturált vezérlési (IF/THEN/ELSE, DO CASE, ...) és objektumorientált eszközöket (rekordok, uniók, osztályok, halmazok) bocsát a programozó rendelkezésére. A mikroassembler (microassembler) mikroprogramok fejlesztését segíti, amelyek alacsony szinten vezérlik a számítógépet. A meta-assembler (meta-assembler) szintaktikai elemző, ami a nyelv szintaktikai és szemantikus leírása alapján assemblert generál a nyelvhez.
Assembler Az assembly nyelvű kód fordítóját assemblernek nevezik. A magas szintű nyelvek fordítója is tartalmaz assemblert. Ez hozza létre a végleges bináris kódot, lefordítva a mnenonikonokat, a műveleti szintaxist és a címzéseket bináris megfelelőjükre. Ebben a reprezentációban jelen vannak a műveleti kódok (opcode), más vezérlőbitek és adatok. Az assembler kiszámítja a konstans kifejezéseket, feloldja a szimbolikus neveket.David Salomon (1993). Assemblers and Loaders A szimbolikus hivatkozások kulcsfontosságúak az assembler programozásban, megkímélik a programok íróit a hosszas számításoktól és attól, hogy mindig hozzá kelljen igazítani a címeket a program módosításaihoz. A legtöbb assembler makrókat is kezel, ami a C prekompilerjéhez hasonlóan szöveges helyettesítést tesz lehetővé. A hívott szubrutinok helyett a makrók helyben (inline) fejtődnek ki.
Egyes assemblerek még optimalizálni is tudnak. Erre konkrét példa több terjesztő mindenütt jelenlevő x86-assemblerjei. A legtöbbjük át tudja alakítani az ugró utasításokat; kérésre például helyettesítenek egy hosszú ugrás valahány kisebb ugrással. Mások átrendezik az utasításokat, vagy beszúrnak, törölnek, összevonnak, mint például a RISC architektúrák assemblerjei, amelyek érzékenyen ütemeznek is, hogy a program még hatékonyabb legyen.
Az assemblerek voltak az első, valóban használt programozási nyelvek, mivel könnyebb hozzájuk fordítót készíteni, mint a magasabb szintű nyelvekhez, hiszen a kód összes eleme viszonylag egyszerűen továbbalakítható bináris kóddá, és nem igényel alaposabb elemzést. Több fordító és félautomata kódgenerátor létezik, amelyek mind az assemblyre, mind a magasabb szintű nyelvekre hasonlítanak. Ezek közül egy jó példa a speedcode.
A különböző architektúrák assemblerjei hasonlóak, de szintaxisukban különböznek. Például ha az utasításban a memóriában tárolt értéket hozzá kell adni egy regiszter értékéhez, akkor az az x86-os Intel processzorcsaládnál add eax,[ebx]. A Gnu Assembler AT&T szintaxisában ez addl (%ebx),%eax. Különbségeik ellenére általában ugyanazt a gépi kódot generálják. Az assemblernek több különböző módja lehet a különféle szintaxisok támogatására.
Menetszám Az assemblert meneteinek száma jellemzi, vagyis azt, hogy hányszor olvassa végig a lefordítandó kódot. Az egymenetes assembler egyszer olvassa végig a kódot. A definíció előtt használt szimbólumokat, például ugrási címkéket külön listára gyűjti. A linker vagy a betöltő (loader) ez alapján tudja, hogy vissza kell térnie a definiálás előtti helyre, és ott a fenntartott helyre írni. A többmenetes assembler az összes szimbólumot tartalmazó listát készít az első menet alkalmával, és a további menetekben ezt a listát használja, és bővíti további információkkal.
Az assemblernek ki kell számítania a memóriacímeket is; ehhez a különböző utasítások méreteivel is számolnia kell, mert nem ugyanakkorák. Ha az utasítás mérete függ az operandusok típusától, akkor pesszimista becslést készít. Szükség esetén a fölös helyet kitölti, vagy tár optimalizáció esetén újraszámolja a címeket.
Az egymenetes assembler a korai időkben gyorsabb volt, mivel a kódot mágnesszalag vagy lyukkártyák okozták, és a visszacsévélés vagy a visszalapozás jelentősen több időt igényelt. Ma már ez nem probléma; sőt, most már a többmenetes assemblerek vannak előnyben, mivel gyorsabbá teszik a linkelést vagy a betöltést.
Példa: A következő kódban az egymenetes assembler meg tudja határozni a BKWD referenciát, amikor az S2 utasítást dolgozza fel. Viszont nem tudja az FWD címét, amikor az S1 utasítással foglalkozik, így a FWD felkerül a listájára. A kétmenetes assembler az első menetben meghatározza a címeket, tehát ezek ismeretében generál kódot a második olvasásra.
S1 B FWD ... FWD EQU * ... BKWD EQU * ... S2 B BKWD
Magas szintű assemblerek A magas szintű assemblerek további nyelvi absztrakciókat tesznek lehetővé: Függvények és eljárások magas szintű deklarálása és hívása Fejlettebb vezérlési szerkezetek Magasabb szintű absztrakt adattípusok, mint rekordok, uniók, osztályok és halmazok Fejlettebb makrófeldolgozás, bár ezt egyes típusok már az 1950-es évektől is tudták Objektumorientált programozási eszközök, osztályok, objektumok, absztrakt osztályok, öröklődésHyde, Randall. "Chapter 12 – Classes and Objects". The Art of Assembly Language, 2nd Edition. No Starch Press. © 2010.
Formai jegyek Az assembly nyelvet utasítások (tényleges kódot hoznak létre) és direktívák, vagy pszeudoutasítások (a fordítás vagy kódgenerálás vezérlése) alkotják. A kód tartalmaz adatokat és lehetnek benne kommentek is.
A különböző processzorcsaládok utasításkészlete bár sokszor jelentősen eltér, de a gyártók által megadott assembly nyelvű szintaxis általában hasonló irányelvekre épül. Az utasítások általában néhány betűs rövidítések, azoknak a gépi utasításoknak a mnemonikjai, amelyek a processzor utasításkészletét alkotják. Az assemblerek közötti különbség tükrözi a gépi kódok közötti különbséget; különböznek a műveletek, a regiszterek száma, mennyisége és típusaik, továbbá az adatok reprezentációja is. Az általános célú számítógépek különböznek abban, hogyan valósítják meg ugyanazt a funkcionalitást. Még ugyanahhoz az utasításkészlethez is létezhet több assembly, amiket különböző assemblerekkel lehet fordítani, de általában a gyártó által szállított a legnépszerűbb.
A direktívákkal vezérelhető a változók és a program elhelyezése, igazítása, a program belépési pontjának meghatározása. A direktívák hatására létrejövő információk egy részét a fordító szintaktikai ellenőrzéshez használja, más részük a szerkesztő (linker) és/vagy a betöltő (loader) program számára ad információt. Ez a két program teszi lehetővé, hogy az assembler által készített kódból futtatható program jöjjön létre.
Az utasítások általában úgy néznek ki, hogy először jön a művelet opkódja (opcode), majd következnek az adatok, paraméterek vagy argumentumok. Az assembly forráskód soronként logikailag egy műveletet tartalmaz, egészében nézve a forrás felépítése a következő: Deklarációs rész: változók, konstansok, makrók definiálása. Végrehajtható, ill kód-rész: utasítások egymásutánja. Egy utasítássorhoz klasszikus esetben egy gépi kódú utasítás (és annak esetleges paraméterei) tartozik, az újabb, úgynevezett makró assemblerekkel definiálhatunk nevesített kódrészleteket is, mintegy „magasabb szintre emelve” ezáltal a nyelvet. Címke: „megcímkézhetünk” egy utasítást, melyet ugró utasítások célpontjaként, esetleg változók illetve konstansok azonosítására használhatunk. Az elvégzendő művelet (operátor) megnevezése (mnemonikja) Egy szóköz után az esetlegesen szükséges operandus(ok) (paraméterek), több operandust hagyományosan vesszővel választunk el. Az operandus(ok) előtt vagy után speciális jelölések mutatják a címzési módot. Megjegyzések, ezeket szintaxistól függő elválasztó karakter után írva rögzíthetjük. Utasítástípusok
Egy processzornak vagy műveletvégzőnek általában 50-80 mnemonikkal megkülönböztetett végrehajtható utasítása van, de ez nagyon eszközfüggő.
Az assemblyt assembler fordítja. Disassemblerrel elvégezhető a fordított irányú művelet is, mivel a megfeleltetés egyszerű az assembly és a gépi kód között; az utasítások általában egy az egyhez fordulnak. Ezzel szemben vannak például makrók, amelyek bővítik az utasításkészletet. Például, ha nincs a gépi kódban olyan utasítás, hogy "ágazz el, ha nagyobb-egyenlő", akkor ezt nyújthatja az assembler, mint "vond ki belőle" és "ágazz el, ha nulla vagy nagyobb". A legtöbb assembler makrók gazdag készletét bocsátja rendelkezésre, amivel bonyolultabb kód és összetettebb adatszerkezetek hozhatók létre. Példa