From Wikipedia, the free encyclopedia
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.
Ezt a szócikket át kellene olvasni, ellenőrizni a szöveg helyesírását és nyelvhelyességét, a tulajdonnevek átírását. Esetleges további megjegyzések a vitalapon. |
Ez a szócikk nem tünteti fel a független forrásokat, amelyeket felhasználtak a készítése során. Emiatt nem tudjuk közvetlenül ellenőrizni, hogy a szócikkben szereplő állítások helytállóak-e. Segíts megbízható forrásokat találni az állításokhoz! Lásd még: A Wikipédia nem az első közlés helye. |
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.
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:
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.[1] 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.
Az assemblert meneteinek száma jellemzi, vagyis azt, hogy hányszor olvassa végig a lefordítandó kódot.
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.[2]
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
A magas szintű assemblerek további nyelvi absztrakciókat tesznek lehetővé:
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.[4] 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ő:
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.
Az alábbi utasítás azt mondja az x86/IA-32 processzornak, hogy mozgasson egy 8 bites értéket egy regiszterbe. Ennek az utasításnak a kódja 10110, amit a regiszter három bites azonosítója követ. Az AL regiszter kódja 000, így a következő gépi kód az AL regiszterbe a 01100001 adatot tölti:[5]
10110000 01100001
Ez a kód ember számára jobban olvasható hexadecimális formában:
B0 61
ahol B0
azt jelenti, hogy mozgasd a következő értéket az AL regiszterbe, és 61
az érték tizenhatos számrendszerben, ami tízes számrendszerben 97. A 8086-os család assemblyje az efféle utasításra a könnyebben megjegyezhető MOV mnenonikot nyújtja. Ezzel a fenti kód assemblerben, és megjegyzéssel:
MOV AL, 61h ; Az AL regiszterbe a 97 decimális számot tölti (61 hexadecimális)
Egyes assemblyk minden adatmozgatást a MOV utasítással fejeznek ki, míg másoknak erre több is van, attól függően, hogy honnan hová kell másolni. Például L a szóban forgó adat regiszterbe mozgatására, ST regiszterből a memóriába, LR egyik regiszterből a másikba, MVI közvetlenül a memóriába.
Az x86-os 10110000 (B0) opkód egy nyolc bites értéket mozgat az AL regiszterbe, 10110001 (B1) a CL-be, és 10110010 (B2) a DL-be. Assembly nyelvű példák következnek.[5]
MOV AL, 1h ; Az AL regiszterbe az 1 betöltése
MOV CL, 2h ; A CL regiszterbe a 2 betöltése
MOV DL, 3h ; A DL regiszterbe a 3 betöltése
A MOV szintaxisa bonyolultabb is lehet, ahogy a következők mutatják:[6]
MOV EAX, [EBX] ; Az EBX tartalma által meghatározott memóriacímről 4 bájtot mozgat az EAX regiszterbe
MOV [ESI+EAX], CL ; A CL tartalmát az ESI+EAX által meghatározott memóriacím 1 bájtjára másolja
Mindezekben az esetekben az assembler a MOV mnemonikot közvetlenül a 88-8E, A0-A3, B0-B8, C6 vagy C7 opkódok valamelyikére fordítja. A program írójának nem kell tudnia, mikor melyiket kell használni.[5]
Az assemblerek szerzői különféleképpen kategorizálják az utasításokat és különböző elnevezéseket használnak. Vannak, akik a mnemonikokon kívül mindent pszeudooperációnak (pseudo-operation, pseudo-op) neveznek. Egy assembly nyelv tipikusan a következőket tartalmazza:
Ezek az utasítások az operatív memóriával közvetlenül kapcsolatos olvasó és/vagy író utasításokat valósítják meg. Minden esetben legalább egy operandusuk van (egy címes gépek esetén), ami az adott memóriacímre hivatkozás (bár ez a cím lehet implicit operandus is, vagyis már adott regiszterben előkészített; nem pedig a műveleti kód adott mezőjében megadott). Az olvasási utasítások a kijelölt memóriacím tartalmat egy kijelölt regiszterbe töltik, míg az írási utasítások a kijelölt regiszter tartalmát tárolják el a kijelölt memóriacímen; továbbá léteznek memória-memória utasítások is, melyekkel egy memóriarekeszt vagy tartományt mozgatnak át (egyes helyeken string-kezelő utasításokként említik őket). Külön utasítások létezhetnek a byte, szó (2 byte) duplaszó (2 szó) stb. tartományok elérésére, de lehetséges, hogy ezt előtéttagok – más néven prefixum – segítségével kell elérni. Ezek – formailag – az utasítás előtt helyezkednek el, és egy utasítás előtt egyszerre több is lehet belőlük (a maximális szám processzorfüggő). Tehát a memóriareferens utasítások kimenetelét illetve lezajlását különböző előtéttagok befolyásolhatják:
A regiszterek között végezhető műveleteket (regiszterek közötti csere, regiszter jobb-bal oldalának cseréje, speciális regiszterekhez való hozzáférés stb.) valósítják meg.
Ezek az utasítások aritmetikai műveletek (összeadás, kivonás, szorzás, osztás, esetleg ugyanezek lebegőpontos változatai, kiegészítve az ún. normalizáló utasítással), illetve elemi logikai műveletek (és, vagy, nem, kizáró-vagy) végrehajtására szolgálnak.
Általában ide sorolják az ún. léptetési utasításokat is, mivel ezek – a bináris számrendszer speciális esete miatt – épp 2 egész számú hatványaival való szorzás, osztás műveletet valósítanak meg.
Ezen kívül az inkrementáló és dekrementáló utasításokat is ide soroljuk.
Az ugró utasításokkal a program végrehajtásának folyamata vezérelhető. Általában feltétel nélküli és feltételes ugrási utasításokról beszélünk. A feltételek egy regiszter, vagy az úgynevezett program- vagy processzorjelző (flag) adott bitjének/bitjeinek állapotához köthetőek (nulla, nem nulla, pozitív, negatív, kisebb, nagyobb, van túlcsordulás, van átvitel stb.). Az egyes utasítások leírásánál pontosan meg van adva, hogy a processzorjelzőket hogyan módosítják a végrehajtás során. (A módosító hatásúak legtöbbször aritmetikai-logikai műveletek vagy kifejezetten erre a célra szánt utasítások.)
Az utasítások operandusa az a hely (vagyis egy cím), ahol a programot folytatni kell, a feltételtől függően. Ha a feltétel nem teljesül, akkor a program a következő utasítást hajtja végre, tehát a végrehajtás folyamata nem módosul. Maga a címoperandus lehet literális – vagyis címke vagy címke kifejezéssel megadott cím – vagy regiszterben kijelölt, esetleg valamilyen indirekció. Továbbá a feltételes ugrásoknál előfordul implicit operandusú megoldás, vagyis amikor az ugrási címet nem adjuk meg konkrétan. Ilyen elágazó utasítás esetén a végrehajtás mindig a (program)sorban következő vagy a következő utáni utasításra helyeződik át – az elágazástól függően.
Speciális utasítások a megállító (halt, sleep), az üres (nop) utasítás és egyes processzorállapot-kezelő utasítások.
Megállítja a program futását, és csak "külső beavatkozás" (pontosabban interrupt vagy reset) hatására lép tovább. Egyes processzorok ekkor automatikusan energiatakarékos üzemmódba lépnek.
Az üres utasítás "nem tesz semmit": a regiszterek tartalma nem változik, az állapotregiszter(ek) jelzőbitjei sem változnak (ha vannak). A üres utasítást általában a NOP vagy NOOP mnemonikkal jelölik, ami az angol no operation rövidítése. A NOP utasítás általában egy operandusok nélküli utasítás, az utasításkészlet alap bithosszának megfelelő kódot kap, pl. a 8 bites Z80 processzornál az egybájtos 0x00 kód, a 32 bites ARM A32 architektúrában a négybájtos 0x00000000 kód, a Motorola 68000-es családban a 2 bájtos 0x4e71 kód reprezentálja. Egyes architektúrákban megvalósítása csak a programszámlálót növeli és ezen kívül semmi más tevékenység nem történik. Végrehajtási ideje lehet az alapvető utasításciklus, de pl. a MOS Technology 65xx processzoroknál két órajelciklusig tart. Ezt a műveletet általában időzítésre használják (valamilyen hardver időzítés miatt, elágazott programszálak futási idejének kiegyenlítésére esetleg hardver diagnosztikai célra → címsín vizsgálatához).
A megszakítások kezelésére szolgáló, valamint a ki- és beviteli műveletek, illetve egyéb, a számítógép és/vagy a processzor működését vezérlő utasítások tartoznak ebbe a csoportba.
Az adat direktívák adatelemeket definiálnak változók és konstansok tárolására. Definiálják az adatok típusát, hosszát és igazítását (alignment). Meghatározzák az adatok láthatóságát is, vagyis azt, hogy kívülről is elérhetők-e, vagy csak abból a kódszakaszból, ahol definiálták őket. Néhány assembler pszeudooperációnak tekinti.
A direktívák az assemblernek szóló utasítások, amelyekből nem keletkezik gépi kód. Pszeudooperációnak is nevezik őket.[1] Az assembler működésének befolyásolásával hatnak a tárgykódra, a szimbólumtáblára, a listákra és az assembler belső paramétereire. Vannak, akik a nyelv részei közül csak azokat nevezik pszeudokódnak, amelyek tárgykódot generálnak, például adatot.[7]
Nevük sokszor ponttal kezdődik, hogy megkülönböztessék őket a gépi utasításoktól. Lehetővé teszik, hogy különböző paraméterekkel máshogy forduljon a program, például különféle célokra. Javíthatják az olvashatóságot és segíthetik a karbantartást. További lehetőség a tárhely fenntartása a futásidejű adatok számára, esetleg még inicializálásuk is.
A szimbolikus assemblerek lehetővé teszik, hogy a programozó neveket használjon, ezzel címkéket vezessen be, vagy változókat és konstansokat definiáljon. Az utasítások ezután használhatják is a neveket, támogatva a kód öndokumentálását. A kódban minden szubrutin nevét a belépési ponttal azonosítják, így azok nevükkel hívhatók. A címkék ugró utasítások céljai lehetnek, és bevezetésük előtt is használhatók. Néhány assembler a címkéket formájuk szerint is lexikálisan elkülöníti a többi szimbólumtól, például 10$.
Egyes assemblerek, mint a NASM rugalmas szimbólumkezelést nyújtanak, így definiálhatók névterek, automatizálható a memóriacímek kiszámítása az adatszerkezetekben, a címkék pedig hivatkozhatnak literális értékekre vagy az assembler által elvégzendő számítások eredményére. A címkék értékül is adhatók változóknak és konstansoknak.
Ahogy a legtöbb programnyelv, az Assembly is lehetővé teszi a kommentelést. A megjegyzéseket az assembler nem fordítja le. Itt le kell írni a kódszakasz jelentését és célját, különben nehéz megérteni, hogy mit csinál a programszakasz. A fordítók és a disassemblerek által generált nyers, megjegyzések nélküli assemblyt nagyon nehéz értelmezni.
A direktívák néhány fontosabb csoportja:
PRINT ON ; /360
LOAD cím ; Z80: program (betöltési) címe ORG cím ; Z80: program (futáskori) címe SEGMENT AT cím ; x86: memóriaterület címe END címke ; Program vége, egyben belépési pont megadása
snév SEGMENT CODE ; x86: kódszegmens snév DSECT ; /360: 'dummy' adatszegmens snév CSECT READ ; /360: kódszegmens, írásvédett snév AMODE 31 ; /370: csak 31 bites módban futtatható
ASSUME CS:code,DS:data,ES:video ; 8086: definíció ASSUME ES:nothing ; 8086: visszavonás USING code,R12 ; /360: definíció DROP R12 ; /360: visszavonás
ENTRY rutin ; /360 EXTRN valtozo ; /360
var EQU érték ; általános var = érték ; /8086
Sok assembler támogat előre definiált makrókat, és vannak, amelyek lehetővé teszik makrók definiálását is. Ezek szövegszerűek, lehetnek bennük utasítások, direktívák, változók és konstansok. Ha az assembler egy makróhoz ér, akkor kifejti, majd folytatja a kifejtett kód feldolgozásával. Egyes makrók több mélységben támogatják a makrók kifejtését. A makrók eben az értelemben az 1950-es évek IBM Autocode Assemblerjeire vezethetők vissza.
Hasonlóan működik a C és a C++ előfeldolgozója, de ott csak egy szint fejtődik ki, továbbá a makrók (többnyire) egy sorosak. Az assembler makrók gyakran hosszabbak, néha hosszú programok, amelyeket az assembler értelmezőként hajt végre. Hasonló a helyzet a PL/I makróival is. Mivel a makrók rendszerint hosszúak, a töredékére tudják rövidíteni a kódot, és a magas szintű nyelvekhez hasonlóvá tudják tenni. Magasabb szintű szerkezetek hozzáadására is alkalmasak.
A makróknak gyakran lehetnek paramétereik is. Némely assembly kifinomult makrónyelvet ágyaz be, magas szintű nyelvi elemekkel, opcionális paraméterekkel, szimbolikus változókkal, feltételekkel, aritmetikával, a környezet elmentésének lehetőségével és információcserével. Az argumentumoktól függően számos assembly utasítássá és adatdefiníciókká fejtődik ki. Implementálhat egész algoritmusokat, rekordszerű adatszerkezeteket, vagy akár vezérlési szerkezeteket. Ha az assemblyt számos ilyen makró bővíti, akkor egy magas szintű nyelvet kapunk, mert a programozók nem közvetlenül a legalacsonyabb szintű fogalmi elemekkel dolgoznak. Makrókkal implementálták a SNOBOL4 egy korai virtuális gépét (1967), amit teljes egészében a SNOBOL Implementation Language (SIL) nyelven írtak, amit makró assemblerekkel írtak át natív assemblerre.[8] Ez hordozhatóvá tette egy hosszabb időre.
Makrókat használtak nagy programrendszerek konfigurálására, például operációs rendszerek vevőre szabott saját verzióihoz is. Például így állították be az IBM rendszerprogramozói is a következőket: Conversational Monitor System / Virtual Machine (VM/CMS), IBM valós idejű tranzakciófolyamatai, Customer Information Control System CICS, ACP/TPF légiforgalmi és pénzügyi rendszer. Ez utóbbi az 1970-es évektől létezik, és nagy lefoglalási rendszereket (CRS) és hitelkártya-rendszert irányít még ma (2017) is.
Lehetséges más nyelveket pusztán assembler makrókkal implementálni, így lehetséges például COBOL programot írni assemblyben. Az IBM OS/360 rendszergenerálásra használja. A felhasználó az opciókat assembler makrók sorozatának kódolásával adja meg. Ezek fordítása a rendszer munkafolyamatai közé tartozik.
Ennek az az oka, hogy a makrók feldolgozása független az assemblytől; az előbbi inkább szövegfeldolgozás, mint kódgenerálás. Hasonlóan működik a C preprocesszora, ahol a makrókkal lehet konstansokat definiálni, és feltételt vizsgálni, de hiányzik belőle a ciklus, a goto és a rekurzió lehetősége, emiatt a C makrók nem alkotnak Turing-teljes nyelvet. A makrók nincsenek tekintettel a nyelvi elemekre, így a legtöbb magas szintű nyelvben nem bátorítják használatukat, mert rendelkezésre állnak más eszközök is, de az assemblyben hasznosságuk miatt megmaradtak. Azonban még az assembler makrók esetén is figyelni kell arra, hogy paraméterként csak név adható meg, és az bemásolódik a paraméter helyére. A kifejezések megadása nevezetes programozási hiba.
Példa:
foo: macro a load a*b
a makró egy egyszerű nevet vár, és egy globális b változót vagy konstanst, amivel megszorozza az "a"-t. Ha ehelyett a foo makrót az a-c
paraméterrel hívják meg, akkor az eredmény load a-c*b
lesz. Az efféle félreértések elkerülése érdekében a makrón belül a paramétereket zárójelezni kell.[9]
Egyes assemblerek beépítetten tartalmazzák a strukturált programozás vezérlőszerkezeteit. Erre a legkorábbi példa a Dr. Harlan Mills által 1970 márciusában javasolt Concept-14 makrókészlet, amit Marvin Kessler implementált az IBM's Federal Systems Divisionnél. Ez az S/360 makró assemblert bővítette.[10] Célja a spagettikód elkerülése, és a GOTO használatának visszaszorítása. A megközelítés az 1980-as évek elején vált általánosan elfogadottá.
A terv egy érdekes megvalósítása volt az A-natural stream-orientált assembly a 8080/Z80 processzorok számára. A Whitesmiths Ltd. fejlesztette ki; az ő termékük volt az Unix-szerű Idris operációs rendszer, és ők készítették az első kereskedelmi C fordítót. A nyelvet assemblyként kategorizálták, mivel nyers gépi elemekkel működött, azonban kifejezésszintaxisa magában foglalta a műveletek sorrendjének meghatározását. Erre zárójelek és más speciális szimbólumok, továbbá blokkos strukturált programszerkezetek szolgáltak. Beépítették a C-fordítóba, kézzel keveset programoztak vele, habár logikus felépítése népszerűvé tette.
Mivel az assemblyt már nem használják széles körben, kevésbé van igény fejlettebb assemblerekre.[11] Ennek ellenére a nyelv nem tűnt el; akkor használják, ha az erőforrások szűkössége vagy a célrendszer jellegzetessége miatt a magasabb szintű nyelvek nem hatékonyak.[12]
A makrókat jól kezelő assemblerek makrók segítségével lehetővé teszik a strukturált programozást, például a Masm32 csomag biztosítja a switch szerkezetet. Egy teljes program:
include \masm32\include\masm32rt.inc ; use the Masm32 library
.code
demomain:
REPEAT 20
switch rv(nrandom, 9) ; generate a number between 0 and 8
mov ecx, 7
case 0
print "case 0"
case ecx ; in contrast to most other programming languages,
print "case 7" ; the Masm32 switch allows "variable cases"
case 1 .. 3
.if eax==1
print "case 1"
.elseif eax==2
print "case 2"
.else
print "cases 1 to 3: other"
.endif
case 4, 6, 8
print "cases 4, 6 or 8"
default
mov ebx, 19 ; print 20 stars
.Repeat
print "*"
dec ebx
.Until Sign? ; loop until the sign flag is set
endsw
print chr$(13, 10)
ENDM
exit
end demomain
A különféle architektúráknak, platformoknak, processzoroknak eltérő, egymással általában semmilyen kompatibilitást nem biztosító assembly nyelvei vannak, bár ezen nyelvek alapszerkezete nagyon hasonló.
Példa (C eredeti: *p = n):
MOV EDX,[EBP-16] MOV EAX,[EBP-20] MOV [EDX],EAX
Példa (ugyanaz, mint az előbbi):
mov -16(%ebp),%edx mov -20(%ebp),%eax mov %eax,(%edx)
A MOS Technology 6510 (pontosabban, a 65xx, 75xx, 85xx processzorcsalád) a Commodore 64 és társai (mint amilyen a C16, VIC-20 vagy a Commodore 128) processzora. Három általános célú nyolc bites regiszterrel rendelkezik: az A akkumulátorral, valamint az X és Y indexregiszterekkel. Címtartománya 64 kilobyte, de külön címzési módok támogatják az első 256 bájt (00xxH címek, az úgynevezett zero page, nullás lap) elérését, a második 256 bájt (01xxH címek) pedig rögzítetten a verem (stack) helye (ezért az S jelű veremmutató regiszter is csak nyolc bites).
Ezek a processzorok főként a Commodore Amiga és a korai Apple Macintosh gépekben váltak ismertté.
Az Intel 8080 adaptációjaként a Zilog cég által gyártott, és korának legelterjedtebb processzorának nyelve. Számos összetettebb utasítással is rendelkezik. A Z80 assembly nyelve az Intel 8080-asénak egy kibővített és egyben egyszerűsített változata, például az összes adatmozgató utasítás mnemonikja egységesen LD, szemben a 8080 különféle változataival, például:
8080 Z80
----- -----
MOV A,B LD A,B
MOV A,M LD A,(HL)
MOV M,A LD (HL),A
LDAX B LD A,(BC)
STAX B LD (BC),A
MVI A,n LD A,n
LXI H,nn LD HL,nn
LHLD cím LD HL,(cím)
Az IBM mainframe-ei processzorainak nyelve. Tizenhat általános célú 32 bites regisztere van (R0-R15), a használható címtartomány eredetileg 24 bites volt (16 MiB címtartomány), a System/370-es sorozattól ez 31 bitesre bővült (2 GiB címtartomány). A gépi utasítások 2, 4 vagy 6 byte hosszúak, a használható adattípusok: byte, félszó (16 bit), szó (32 bit), duplaszó (64 bit), pakolt és 'zónázott' decimális szám (1-16 byte), általános memóriaterület (1-256 byte). Kétcímű gép, azaz egy utasításban két memóriaterület is szerepelhet.
Példaprogram (egy eljárás tipikus kezdete/vége/hívása):
RUTIN STM R14,R12,12(R13) ; regiszterek mentése a hívó
; regisztermentési területére
BASR R12,R0 ; bázisregiszter beállítása
USING *,R12 ; assembly direktíva:
; használd a bázisregisztert
LA R14,SAVEA ; saját mentési területünk
ST R13,4(R14) ; elmentjük a hívóét
LR R13,R14 ; használjuk a sajátunkat
…
KILÉP L R15,visszatérési_érték
L R13,4(R13) ; vissza a hívó mentési területére
L R14,12(R13) ; visszatérési cím
LM R0,R12,20(R13) ; hívó regisztereinek visszatöltése
; kivéve az R15-öt
BR R14 ; visszatérés
SAVEA DS 18F ; regisztermentési terület: 72 byte
…
HÍVÁS L R15,=A(RUTIN) ; rutin címe R15-be
LA R1,paraméterek_címe
BASR R14,R15 ; ugrás R15-re,
; visszatérési cím R14-be
UTÁNA LTR R15,R15 ; visszaadott érték vizsgálata
BZ SIKER ; tipikusan 0=siker
Minden programozható eszközre van assembler, némelyikre akár több tucat.
Unix rendszereken az assembler neve hagyományosan as. Ez nem egyetlen kódtest, hanem minden portra más. Unix változatok sora a GNU Assemblert használja, amit néha a gas névvel illetnek. Ez egy keresztassembler, ami számos gazdagépen számos célgép számára fordít.
Néhány assembler képes más assembler nyelvjárását olvasni, például a TASM a régi MASM-kódot, és abból tud TASM-kódot készíteni, de ezt nem tudja a másik irányba megcsinálni. A FASM és a NASM szintaxisa hasonló, de más makrókat támogatnak, ezért nehéz átírni egyiket a másikra. Az alapok azonosak, de a fejlettebb eszközök eltérnek.[13]
A sok különféle nyelvjárás ellenére néha hordozható az assembly program a különféle operációs rendszerek és az azonos típusú processzorok között. A különféle operációs rendszerek hívási konvenciói gyakran alig vagy egyáltalán nem különböznek. Egy utasításkészlet szimulátor bármely assembler binárisát vagy tárgykódját fel tudja dolgozni, így segít hordozhatóvá tenni a kódot a különféle platformok között, és az időveszteség nem nagyobb, mint egy tipikus bájtkód értelmezőnél. Ez hasonló ahhoz, hogy mikrokódokkal elérni a hordozhatóságot egy processzorcsalád tagjai között.
Egyes magasabb szintű nyelvek, mint a C vagy a Pascal lehetővé teszik assembly kód inline használatát. Az assembly kódszakaszok lehetnek több sorosak, de rendszerint rövidek. A Forth nyelvben ezek CODE szavakban használhatók.
Az assembly nyelvű programok javításához emulátorok használhatók.
Az összehasonlítást a magas(abb) szintű programozási nyelvekkel szemben kell érteni.
Az assembly egyidős a tárolt programot futtató számítógépekkel. Előnye a gépi kóddal szemben az, hogy könnyebben megjegyezhető, és gyorsabban írhatók vele programok. A programozóknak nem kell számkódokat megjegyezniük és memóriacímeket számolniuk, mert azt az assembler elvégzi. A korai időktől kezdve széles körben használták. Az 1980-as években visszaszorult, de azóta sem tűnt el teljesen. A mikroszámítógépeken az 1990-es években szorult vissza.
A történelmi időkben számos program készült csak assemblyben, például operációs rendszerek. Az első más nyelven írt operációs rendszer a Burroughs MCP (1961) volt, ami teljes egészében egy Algol nyelvjárásban, az Executive Systems Problem Oriented Language (ESPOL) nyelven készült. Kereskedelmi programok is készültek assemblyben, például az IBM mainframe szoftver, amit nagy cégek írtak. Nagyobb szervezetek egész sora az 1990-es évekig ragaszkodott az assemblyhez, habár addigra elterjedtek más nyelvek, mint COBOL, FORTRAN és PL/I.
A legkorábbi mikroszámítógépek a kézzel kódolt assemblyre hagyatkoztak, beleértve a legtöbb operációs rendszert és nagy programot. Erre azért volt szükség, mert az erőforrások szűkösek voltak, sajátos típusú memóriájuk és képernyőjük, továbbá rendszerszolgáltatásaik is korlátozottak voltak. Döntőnek bizonyult, hogy nem voltak fordítók a magas szintű nyelvek számára; és ahelyett, hogy írtak volna, inkább maradtak az assemblynél. Emellett a mikroszámítógépek programozóinak első nemzedéke lélekben hobbiprogramozó maradt.
Kereskedelmi szempontból az assembly előnyei voltak a kis méret, a kevés adminisztráció, a gyorsaság és a megbízhatóság.
A korszak jellemző assembly nyelvű programjai voltak az IBM PC DOS operációs rendszerei és a Lotus 1-2-3 táblázatkezelők. Még az 1990-es években is assemblyben íródott a legtöbb videójáték, többek között a Mega Drive/Genesis számára készültek, és a Super Nintendo Entertainment System. Egyes fejlesztők véleménye szerint az assembly volt a leggyorsabb nyelv a Sega Saturn számára, egy konzol, amire kihívás volt programokat, különösen videójátékokat írni.[14] Egy másik példa az NBA Jam (1993) táblajáték.
Sokáig személyi számítógépekre is az assembly maradt a fő nyelv, mivel a BASIC lassú volt, és nem tudta kihasználni a rendszer lehetőségeit. E típusok közé tartozott az MSX, a Sinclair ZX Spectrum, a Commodore 64, a Commodore Amiga, és az Atari ST. Egyes rendszerek beépített assembly fejlesztőkörnyezettel érkeztek, fejlett hibakereséssel és makró rendszerrel.
Az assembly nem versenyez a magas szintű programozási nyelvekkel sajátos szűk körű alkalmazási területei miatt. A TIOBE index listáján is előkelő helyet foglal el, 2016 novemberében a kilencedik helyen állt, ezzel megelőzve például a Swiftet és a Rubyt.[15]
Az assemblerek a méretre és sebességre optimalizálást hatékonyabban végzik, mint a magasabb szintű nyelvek fordítói,[16][17][18] habár vannak, akik ezt vitatják.[19] A modern eszközök, processzorok és memóriák egyre bonyolultabbá teszik minden fordító számára az optimalizálást.[20][21] A processzorok megnövekedett teljesítőképességük miatt sokszor üresen állnak,[22] vagy várakoznak I/O műveletek vagy memória lapozás miatt. Emiatt a legtöbb programozó keveset törődik az optimalizációval.
Manapság legelterjedtebben mikrovezérlő alapú rendszerekben alkalmazzák, mivel ott sokszor maga a futtató eszköz és/vagy annak környezete nem teszi lehetővé magas szintű nyelvek alkalmazását vagy akár fordítóprogramok létrehozását (túl kevés támogatott utasítás, kis méretű memória illetve stack, stb.). Megjegyzendő azonban, hogy a mikrokontroller architektúrák robbanásszerű fejlődése eredményeképp erősen terjed a C, C++ nyelv használta is e téren.
„Nagyszámítógépes” környezetben (PC-k, munkaállomások stb.) a hardverillesztő szoftvert és az operációs rendszert programozók írnak assembly nyelven, más esetekben használata ritka, csak a nagyon méret- vagy időkritikus feladatoknál alkalmazzák.
A legtöbb informatikai képzésen tanítanak assemblyt. Habár már kevesen használják a gyakorlatban, a legalapvetőbb fogalmak, mint kettes számrendszerbeli aritmetika, memóriafoglalás, stack processing, megszakításfeldolgozás, karakterkódolás és fordítótervezés igazán csak alacsony szinten érthetők meg. Mivel a számítógép viselkedése alapvetően utasításkészletétől függ, logikus ezeket egy assemblyn tanulmányozni.
Tehát az assemblyvel megtanulhatók: az alapfogalmak; annak felismerése, mikor érdemes assemblyt használni; és felmérni a magas szintű nyelvekből fordult kód hatékonyságát.[23] Ez megfelel annak, hogy a gyerekek megtanulnak számolni, habár a számológépeket elterjedten használják.
Tipikusan assemblyben készül:
Ez a példa (rutin) a Z80-as (Zilog Z80) mikroprocesszor assemblyjében íródott. Bájtok blokkját másolja át a memória (RAM) egyik helyéről (címéről) a másikra. Értelme a Sinclair ZX Spectrum számítógépen az, hogy a másolás a videomemória célterületére történik (a gyakorlatban arra használták, hogy a például egy sok számítást igénylő kép előállítása ezen a számítógépen sokáig tartott, s ha annak folyamatát a felhasználó elől el akarták rejteni, akkor először a memória egy használatlan területén állították elő a képet, majd ez – nagyon rövidke idő alatt – be lett másolva a videomemória területére. A megjegyzések pontosvesszővel vannak elválasztva a programkódtól.
ld hl, 16384 ; a hl regiszterpárba a videomemória első bájtjának címe kerül
ld bc, 6912 ; a bc regiszterpárba kerül az átmozgatott blokk hossza (a videomemória hossza) bájtban
ld de, 40000 ; a de regiszterpárba a forráscím
loop ld a, (de) ; az a regiszterbe a forrás értéke kerül
ld (hl), a ; a hl regiszterpár értékének a címére a kiolvasott érték kerül
inc hl ; célcím növelése eggyel
inc de ; forráscím növelése eggyel
dec bc ; a hátralévő hossz csökkentése
ld a, b ; ez és a következő sor a vizsgálja, hogy a bc regiszterpár
or c ; értéke 0
jr nz, loop ; ha nem, ugrás vissza (a loop fejlécű sorra)
ret ; visszatérés
Érdekes megfigyelni a tizenhat bites bc regiszterpár nullás értékének vizsgálatát egy nyolcbites regiszterrel, ami két lépésben történik. Először az ld a, b utasítással a b regiszter értéke (mely a bc regiszterpár egyik regisztere) az a regiszterbe töltődik, majd az or c utasítással az a és a c regiszter értékei közt or logikai utasítás hajtódik végre, melynek értéke az a regiszterbe kerül. Ha az eredmény éppen 0, az azt jelenti, hogy a bc regiszterpár értéke is éppen 0 és a jr nz, loop feltételes ugrás nem hajtódik végre (de egyébként igen).
Az Intel 8080-as processzorhoz képest fejlettebb utasításkészletnek köszönhetően az előző példát egyszerűbben is megírhatjuk (és gyorsabb lesz a végrehajtási sebesség is: előző esetben 52 órajel/bájt, következő esetben 21 órajel/bájt):
ld bc,6912 ;bc-ben a hossz bájtokban
ld de,40000 ;de-ben a forrásterület címe
ld hl,16384 ;hl-ben a célterület címe
ldir ;blokkmozgató utasítás automata ismétléssel (21 órajel átvitt bájtonként)
ret
A játékokhoz korábban a végsőkig igénybevették a processzorok számítási teljesítményét. Pl a következő trükkel újabb 25%-nyi időt nyerhetünk (16,5 órajel/bájt):
ld b,0 ;számláló (0=256)
ld de,40000
ld hl,16384
loop ldi ;blokkmozgató utasítás, nem ismétel (16 órajel)
ldi
.
.
.
ldi ;összesen 27 db ldi utasítás egymás után (27*256=6912)
djnz loop ;b-t csökkenti és visszaugrik, ha b<>0 (13 órajel)
ret
Példa "hello.asm" állomány tartalma :
ORG 100H ; ez minden COM allomany elejen jelen kell legyen egy
; COM allomany maximum 64 KB meretu, es egyetlen 64 KB-os
; teruleten van jelen az adat, kod es verem szegmens is;
; az elso 256 byte (100h) a DOS szamara van fenntartva;
; mind a negy szegmensregiszter erteke azonos, így nem
; kell ezeket a legtobb esetben valtoztatnunk
start:
mov dx, uzenet ; DS:DX-be helyezzuk a kiirando uzenet kezdocimet; a
; DS-t mar nem kell beallitani, mert COM allomanyban vagyunk
; az uzenet DOS-os formatumu, azaz $ jellel vegzodik (0x24)
; NASM-ban egy valtozo cimere a nevevel (pl. uzenet) hivatkozunk
; a cimen levo ertekre pedig a [nevevel] (pl. [uzenet]) hivatkozunk
mov ah, 09h ; DOS 21h / 09h - DOS-os string kiirasa
int 21h
xor ah, ah ; egy BIOS megszakitas hivasa: varakozas egy billentyu lenyomasara
int 16h ; leirasert lasd Tech Help! 6.0
int 20h ; a COM programok vegen a 0x20 megszakitas meghivasaval fejezzuk
; be a program futasat es adjuk vissza a vezerlest az operacios
; rendszernek
adatok:
uzenet db "Hello, World!", 0x0D, 0x0A, "$"
Seamless Wikipedia browsing. On steroids.
Every time you click a link to Wikipedia, Wiktionary or Wikiquote in your browser's search results, it will show the modern Wikiwand interface.
Wikiwand extension is a five stars, simple, with minimum permission required to keep your browsing private, safe and transparent.