Assembly programozási nyelv

A Wikipédiából, a szabad lexikonból.

Az assembly programozási nyelv 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 programozási nyelv.

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.

Tartalomjegyzék

[szerkesztés] Keletkezés

Az első számítógépek programozása úgy történt, hogy a számításokat végző elemek huzalozást változtatták meg.

A számítógépeket a kezdetekben a processzoruk 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 vagy hexadecimális számrendszerben (vagy röviden csak „hexában”) történt.

Állítólag az atombomba-számításokat végző Dr. Glennie 1954-ben saját szakállára elkészítette a MARK I elsô assembler fordítóját.

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.

[szerkesztés] Formai jegyek

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 assembly nyelvet utasítások (tényleges kódot hoznak létre) és direktívák, vagy pszeudó utasítások (a fordítás vagy kódgenerálás vezérlése) alkotják. 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.

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éterjövő információk egy részét a fordító szintaktikai ellenőrzéshez használja, más részük a szerkesztő és/vagy a betöltő 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 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.
    1. 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.
    2. Az elvégzendő művelet (operátor) megnevezése (mnemonikja)
    3. 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.
    4. 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ő.

[szerkesztés] Memória referenciális utasítások

Az utasítások az operatív memóriával közvetlenül kapcsolatos olvasó vagy író utasításokot 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. 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 regiszeter tartalmát tárolják el a kijelölt memóriacímen. Általában külön utasítások léteznek a byte, szó (2 byte) duplaszó (2 szó) illetve lebegőpontos számok kezelésére.

[szerkesztés] Regiszterkezelő utasítások

Az utasítások 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. Általában nincs operandusuk, ui. nem igényelnek memóriához fordulást. A érintett regiszterek és a művelet általában az utasítás mnemonikból derül ki.

[szerkesztés] Aritmetikai és logikai utasítások

Az utasítások decimális aritmetikai műveletek (összeadás, kivonás, szorzás, osztás, esetleg ugyanezek lebegőpontos változatai, kiegészítve az un. normalizáló utasítással), illetve elemi logikai műveletek végrehajtására szolgálnak. Ha a művelet nem regiszterek között történik, akkor van operandus: a megfelelő memóriacímet jelöli.

Általában ide sorolják az un. léptetési utasításokat is.

[szerkesztés] Ugró utasítások

A 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ásokat léteznek. A feltételek egy regiszter, vagy az un. program- vagy processzorjelző vagy annak kijelölt bitjeinek állapothoz köthetőek (nulla, nem nulla, pozítí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 program- vagy processzorjelzőket hogyan módosítják. Az utasítások operátora az a hely, ahol a progaramot 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. Speciális utasítások a megállító, és az üres utasítás. Az egyik megállítja a prgram futását, és csak külső beavatkozás hatására lép tovább a program, a üres utasítás pedig "nem tesz semmit".

[szerkesztés] Processzor állapot kezelő utasítások

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ést vezérlő utasítások tartoznak ebbe a csoportba.

[szerkesztés] A nyelv előnyei

Az assembly nyelven megírható szoftver előnyei a magas szintű programozási nyelvekkel szemben:

  • Kisebb méretű kód, mert a programozó pontosan eldöntheti, hogy mi az, ami szükséges a programba, és így nem hajtatunk végre fölösleges dolgokat a programmal.
  • Gyorsabb programfutás, mert nem hajtatunk végre fölösleges dolgokat a programmal, esetleg speciális, architektúra-specifikus trükkökkel gyorsíthajuk az algoritmust a kedvezőbb végrehajtási idő érdekében.
  • Ha minden eljárásunkat mi írtuk, akkor pontosan tudjuk, hogy mit csinál a programunk.
  • Vannak feladatok, amiket csak a gép saját nyelvén lehet hatékonyan kezelni.
  • Sok algoritmus megvalósítása rövidebb assembly nyelven, mint a magas szintű programozási nyelvbéli megfelelője (főként azok, melyek a gépek természetes bináris ábrázolásával kapcsolatosak, mint amilyen a kettő hatványaival való szorzás–osztás, kis egész számokkal való alapműveletek, stb.).
  • A forrás makrókkal egyszerűsíthető.
  • A nagyobb alkalmazások átláthatóságát illetve bővíthetőségét az assembly nyelvű úgynevezett „include”-ok, a futásiidejű dinamikus könyvtárak és a program tárgykódhoz kapcsolódó gépi kódú rutinkönyvtárak teszik lehetővé.

[szerkesztés] A nyelv hátrányai

Hátrányai a magas szintű programozási nyelvekkel szemben:

  • A programozási nyelvek közül legnehezebb programozni, mivel a nyelv teljesen a gép logikáját követi. A makrók használata sokszor körülményes, illetve korlátozott. Az assembly, bármennyire is makrósítjuk, nem lesz könnyen programozható. Egyszerűen nem erre találták ki a nyelvet.
  • A forrásprogramok nehezen olvashatóak, nehezen bővíthetők.
  • Az utasítás készlete nem bővíthető.
  • A hibakeresés nehéz és sokkal időgényesebb, mint a magas szintű nyelvek esetében.
  • A nyelv platform–függő: szorosan kötődik egy bizonyos processzorhoz (és sok ponton kötődik az operációs rendszerhez is).
  • Sokan lebecsülik az „alacsony szintű” megnevezés miatt.

[szerkesztés] Használata napjainkban

Manapság a hardverillesztő szoftvert és az operációs rendszert programozók írnak programot assembly nyelven, más esetekben használata ritka, csak a nagyon méret– vagy időkritikus feladatoknál alkalmazzák.

[szerkesztés] Assembly nyelvjárások

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ó.

[szerkesztés] Intel x86 (8086 - 80486 - Pentium)

  • Intel szintaxis
    • Microsoft Macro Assembler (MASM, Microsoft) – Ingyenesen hozzáférhető a MASM32 projekt részeként [1]
    • Turbo Assembler (TASM, Borland) [2]
    • Netwide Assembler (NASM, GNU) [3]
    • High Level Assembler (HLA, Public Domain) – Randall Hyde-nak, a The Art of Assembly Language című könyv szerzőjének saját assemblere [4]
    • Flat Assembler (FASM, GNU GPL) [5]
  • AT&T szintaxis
    • GNU Assembler (GAS, GNU)

[szerkesztés] MOS-6510

A 6510 a Commodore 64 és társai (mint amilyen a VIC-20 vagy a Commodore 128) processzora. Három regiszterrel rendelkezik: az A akkumulátorral, valamint az X és Y index–regiszterekkel. Címtartománya 64 kilobyte. Utasításkészlete csekély, nagyrészt elemi utasításokkal rendelkezik.

[szerkesztés] Motorola 68xxx

Ezen processzorok főként a Commodore Amiga gépekben váltak ismertté.

[szerkesztés] Z80

A Zilog cég által gyártott, és korának legelterjedtebb processzorának nyelve. Számos összetettebb utasítással is rendelkezik.

[szerkesztés] Egy példa

Ez a példa (rutin) a Z80-as (Zilog Z80) mikroprocesszor assembly-jé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 pl. 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ő bajtjá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álátá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).

A 8 bites processzorhoz képest fejlett 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
Más nyelveken