Kitlei Róbert - Assembly programozás
Országok listája
Hungary
Debreceni Egyetem
Informatikai Kar
Programtervező informatikus
Számítógép Architektúrák
Egyéb Segítség
Kitlei Róbert - Assembly programozás
2009.01.06 14:27:18
Az alábbi szöveg egy formázás és képek nélküli előnézete a dokumentumnak. A tökéletes megjelenítéshez jelentkezz be, majd töltsd le a dokumentumot.
Kitlei Róbert
Assembly programozás
Lektorálta: Dévai Gergely
ELTE, 2007
Kitlei Róbert
Assembly programozás
1
Tartalomjegyzék
Bevezet...................................................................................................................................................................................3 Alapvet adatelemek.................................................................................................................................................................4 Logikai értékek..................................................................................................................................................................................4 Eljel nélküli egész számok..............................................................................................................................................................4 Eljeles egész számok.......................................................................................................................................................................5 Törtszámok........................................................................................................................................................................................5 Karakterek, stringek...........................................................................................................................................................................6 Feladatok............................................................................................................................................................................................7 Az architektúra alapjai..............................................................................................................................................................8 A memória szervezése.......................................................................................................................................................................8 A regiszterek....................................................................................................................................................................................10 Az utasítások szerkezete..................................................................................................................................................................10 Konstansok.................................................................................................................................................................................11 A memória címzése.........................................................................................................................................................................11 Adatmozgatás..................................................................................................................................................................................12 Logikai utasítások............................................................................................................................................................................12 Bitléptet és bitforgató utasítások...................................................................................................................................................12 Aritmetikai utasítások......................................................................................................................................................................13 Vezérlésátadás.................................................................................................................................................................................13 Programozási konstrukciók megvalósítása..............................................................................................................................14 Változók deklarációja, definíciója...................................................................................................................................................14 Értékadás..........................................................................................................................................................................................14 Goto.................................................................................................................................................................................................14 Kifejezések......................................................................................................................................................................................15 Szekvencia.......................................................................................................................................................................................16 Elágazás...........................................................................................................................................................................................16 Rövidzáras operátorok.....................................................................................................................................................................17 Elöltesztels ciklus..........................................................................................................................................................................18 Hátultesztels ciklus........................................................................................................................................................................19 Rögzített lépésszámú ciklus............................................................................................................................................................20 Leszámláló ciklus............................................................................................................................................................................21 Feladatok..........................................................................................................................................................................................22 A futási idej verem szerepe: rekurzív alprogramhívások megvalósítása...............................................................................25 A futási idej verem használata.......................................................................................................................................................25 Konkrét példa a futási idej verem használatára: a faktorialis( 2 ) hívás megvalósítása...............................................................26 Konkrét példa a futási idej verem használatára: a faktorialis függvény kódja.............................................................................26 A veremkeret általános szerkezete..................................................................................................................................................26 Parancssori paraméterátadás...................................................................................................................................................27 A rendszerszolgáltatások elérése: fájlkezelés..........................................................................................................................28 Feladatok..........................................................................................................................................................................................29 Makrók....................................................................................................................................................................................31 Egysoros makrók.............................................................................................................................................................................31 Többsoros makrók...........................................................................................................................................................................33 Feltételes fordítás makrókkal..........................................................................................................................................................35 Feladatok..........................................................................................................................................................................................37 A program fordításának menete..............................................................................................................................................39 A gépi kód szerkezetérl.........................................................................................................................................................40 CISC-elv architektúrák..................................................................................................................................................................41 RISC architektúrák..........................................................................................................................................................................41 Listafájl............................................................................................................................................................................................42 Hibakeresés a programban......................................................................................................................................................43 Gyakran elforduló hibák.......................................................................................................................................................45 A feladatok megoldásai...........................................................................................................................................................49 Értékek ábrázolása...........................................................................................................................................................................49 Utasítások........................................................................................................................................................................................50 Parancssori paraméterátadás, rendszerszolgáltatások.....................................................................................................................57 Makrók.............................................................................................................................................................................................60 Irodalom..................................................................................................................................................................................62
2
Assembly programozás
Kitlei Róbert
Bevezet Az assembly olyan nyelvek gyjtneve, amelyek segítségével számítógépes programok alacsony szint, közvetlenül a processzor által értelmezhet mködése írható le emberek számára olvasható, szöveges formában. Az egyes számítógép-architektúrákon1 található assembly nyelvek nagymértékben különböznek, mivel az architektúrák hardveres felépítése is eltér. A számítógép-családok általában az architektúra minden elírását megtartják a késbbi modellekben is azaz visszafele kompatibilisek , emiatt a korábbi modellekre írt programok változtatás nélkül futtathatóak a késbbieken is. A munkafüzet fleg az x86 architektúra assembly nyelvével foglalkozik, azon belül is a 32 bites szervezés processzorokéval (ezt szokták IA-32 vagy x86-32 architektúrának is nevezni). Az architektúrába tartozó legismertebb gépek a 8086, a 80386 (rövidebben 386-os) és a Pentium. Napjainkban az architektúrába tartozó processzorok két legnépszerbb gyártója az AMD és az Intel. A gépek elterjedtsége miatt a munkafüzetben leírtak könnyen kipróbálhatóak a gyakorlatban is. Bizonyos esetekben elkerülhetetlen, hogy az assembly programozás során igénybe vegyük az operációs rendszer szolgáltatásait. Assembly szintrl ezek eltéren kezelendek, ezért itt is választanunk kell. A munkafüzet a Linux rendszerek néhány fontos szolgáltatását írja le. Ez a választás abból a szempontból nem elsrend, hogy az architektúra határozza meg az assembly nyelv jellegét, azonban ha azt szeretnénk, hogy mködjenek is a programjaink, például tudjunk a képernyre írni, ismernünk kell ennek is a módját, ez pedig az operációs rendszeren múlik. Ha eldöntöttük, melyik platformra (architektúrára, azon belül pedig operációs rendszerre) szeretnénk programokat fejleszteni, ki kell választanunk, melyik assemblerrel és szerkesztprogrammal (linkerrel) dolgozzunk. Az assembler az a program, ami az assembly nyelv forráskódot egy közbüls formátumra, tárgykódra fordítja, a szerkeszt pedig egy vagy több tárgykódból elállítja a gép által közvetlenül értelmezhet futtatható állományt. A legtöbb platformon több assembler és linker is elérhet. A munkafüzetben részletesen a nasm assemblerrel és a gcc szerkesztprogrammal fogunk foglalkozni. A nasm fontos tulajdonságai, hogy a benne írt forráskód szintaxisa könnyen olvasható, az assembler maga pedig ingyenesen elérhet több platformra is. A gcc-t mint szerkesztprogramot szintén elterjedtsége miatt választottuk. A munkafüzet célja az assembly általános jellegének áttekintése egy adott assembly nyelv és környezet segítségével. Ennek érdekében a munkafüzet sok egyszersítést tartalmaz: az architektúra számos elemét, amelyeket a munkafüzetben vázolt alapelevek alapján immár könny megérteni, nem mutatjuk be. Szintén kihagyjuk az architektúra specifikus elemeit, amelyek rendszerprogramozás során szükségesek. A részletek megtalálhatóak a megfelel dokumentációs anyagokban, amelyekhez a hivatkozást lásd a munkafüzet végén.
1 számítógép-architektúra: a számítógéppel szemben támasztott hardveres követelmények leírása, kiemelten a processzor bels struktúrájának, ezen keresztül annak programozásának alapvetése. Az azonos architektúrával rendelkez számítógépeket számítógépcsaládoknak nevezzük.
Kitlei Róbert
Assembly programozás
3
Alapvet adatelemek A számítógépek adatábrázolásának alapeleme a bit. Ez egy olyan tároló, amely egy adott idpillanatban két lehetséges állapot közül az egyiket veszi fel: nullát vagy egyet tartalmaz, köznapi szóhasználatban ,,le van kapcsolva" és ,,fel van kapcsolva". Minden adatot bitekkel reprezentálunk; a következkben áttekintjük, hogy milyen adatokat szoktak ábrázolni biteken, illetve hogyan értelmezendek ezek az adatok, ha csak az ábrázolt alakjukkal találkozunk. Mivel a hardver szintjén csak maguk a bitek léteznek, a programozó felelssége, hogy pontosan tudja a program minden pontján, melyik bit és bitcsoport milyen adatot ábrázol, illetve melyik adatszerkezet része. Logikai értékek Egy bitnek két állása van, ezért nagyon könnyen lehet egy biten egy logikai értéket ábrázolni. A konvenciók szerint a 0 a hamis, az 1 az igaz; a szóhasználatban ezeket egymással felcserélheten használják, pl. ,,0 vagy 1 értéke igaz" ahelyett, hogy ,,hamis vagy igaz értéke igaz" . A logikai értékekkel különböz mveleteket lehet végezni, amelyeket táblázatos alakban szoktak megadni. A mveleteket általában nem egyes bitekre alkalmazzák, hanem bitvektorokra, pozíciónként. and 0 1 0 0 0 1 0 1 or 0 1 0 0 1 1 1 1 xor 0 1 0 0 1 1 1 0
not
0 1
1 0
not 1101 0101 0010 1010
1010 0010 and 0111 1101 0010 0000
0011 1000 or 1001 0001 1011 1001
0100 1001 xor 1111 0110 1011 1111
Eljel nélküli egész számok bitek állása a bit sorszáma a bit helyi értéke 0 7 27 1 6 26 1 5 25 0 4 24 0 3 23 0 2 22 1 1 21 0 0 20
A számok bitjeit a legkisebb helyi értéket ábrázoló bittl (a legalsó bittl) szokták a magasabb helyi értéket ábrázoló (fels) bitek felé, nullával kezdden számozni. Ez azért logikus, mert így a pozíciót egy kettes alapú hatvány kitevjeként értelmezve rögtön adódik a bit helyi értéke, azaz, hogy számként ,,mennyit ér" a bit. A fenti nyolcbites szám értéke: 0 · 27 + 1 · 26 + 1 · 25 + 0 · 24 + 0 · 23 + 0 · 22 + 1 · 21 + 0 · 20 = 011000102 = 9810. Az alsó index a szám számrendszerét jelzi, ezt mindig tizes számrendszerben adjuk meg. A kettes számrendszerbeli számokat szokták binárisnak, a tízes számrendszerbelieket decimálisnak nevezni. Gyakran használatos még a tizenhatos, más néven hexadecimális (rövidítve hexa) számrendszer is, mert ezzel rövidebben le lehet írni a bináris számokat, és ha szükség van rá, kényelmesen oda-vissza lehet alakítani ket a két alak között. A hexadecimális számjegyek: 0-tól 9-ig mint a decimálisban, utána pedig a latin ábécé beti: A, B, C, D, E, F (tízes számrendszerben az értékük sorra 10, 11, 12, 13, 14, 15). A könny egymásba alakíthatóság abból következik, hogy a bitek négyes csoportja pont egy hexadecimális számjegyet kódol. 0 1 2 3 4 5 6 7 0000 0001 0010 0011 0100 0101 0110 0111 8 9 A B C D E F 1000 1001 1010 1011 1100 1101 1110 1111
4
Példa
Assembly programozás
Kitlei Róbert
<<<<<<<<<<<<<<<<<<<<<<<<<<<
Tízes számrendszerbl kettesbe a következ algoritmussal konvertálhatunk. Írjuk le magát a számot. Osszuk el kettvel a számot. Az osztási maradékot írjuk a szám mellé, a hányados egészrészét pedig alá. Folytassuk az eljárást addig, amíg el nem jutunk nulláig. Ekkor a bináris szám a kapott osztási maradékok sorozata alulról felfelé olvasva. Azért nem felülrl lefelé, mert minden egyes osztás egy kettes szorzótényezvel bvíti az új bináris számjegyet, vagyis az n-edik sorban lev bináris számjegy a 2n-es helyi értékhez tartozik. Azaz felülrl lefelé a bitek sorszámai rendre 0, 1, 2, ..., tehát fordítva, alulról felfelé olvasva kapjuk meg a számot. Jelen esetben 4210 = 1010102.
42 0 21 1 10 0 5 1 2 0 1 1
Bináris számokat összeadni a tízes számrendszerben megszokottakhoz hasonlóan lehet. Elindulunk az utolsó pozícióról, és összeadjuk az ott található két számjegyet. A két számjegy összege lesz a két szám összegének utolsó számjegye. Innen a következ pozícióra lépünk, és folytatjuk az összeadást, azonban innentl még egy számjegyet is hozzá kell adnunk az adott pozíción szereplekhez. Ez az átvitel, ami 1, ha az elz összeadás túlcsordult, vagyis az eredménye már nem fért el egy számjegyben, különben pedig 0. 1 0 1 0 0 1 0 0 1 0 0 0 1 1 0 1 + 1 1 0 0 0 0 0 0 1 0 0 1 0 1 1 1 1 0 1 1 0 0 1 0 1 0 0 1 0 0 1 0 0
hexában is megjelenik a 16 biten való túlcsordulás az összeg már túlcsordul a 16 biten 0 2 +0 2 +1 2 átvitel = 01 2 , nincs átvitel 1 2 +1 2 + 1 2 átvitel = 11 2 1 2 +0 2 + 1 2 átvitel = 10 2 1 2 +1 2 =10 2 , átvitel keletkezik 8 16 +9 16 +1 16 átvitel= 12 16 D16 +7 16 = 14 16
els összeadandó második összeadandó
A 4 8 D + C 0 9 7 1 6 5 2 4
Eljeles egész számok
A nemnegatív számok alakja ebben az ábrázolásban megegyezik az eljel nélküli ábrázolás szerintivel. Az összes lehetséges ilyen szám közül most azonban csak azokat a számokat tekintjük érvényes pozitív számnak, amelyeknek a legfels bitje nulla. A negatív számokat a következképpen kaphatjuk meg a pozitív számokból. Írjuk fel az eljel nélkül tekintett számot binárisan, alkalmazzuk rá bitenként a not mveletet, majd növeljük meg eggyel a kapott számot. Ennek a mveletnek negáció vagy negálás a neve. Az eljelet a legfels bit mutatja. Ezzel az eljárással van egy olyan szám, amit nem kapunk meg egyetlen nemnegatív számból kiindulva sem: az egyes bittel kezdd, onnan végig csupa nullát tartalmazó szám. Ennek az értéke eggyel kisebb, mint a fent leírt módon ábrázolható számok közül a legkisebb; n bites szám esetén az értéke pontosan 2n-1. a szám eljel nélkül: 11610 egyes komplemens: ~11610 kettes komplemens: -11610
az eljel leolvasható a legfels bitbl a negáció eltt pozitív (0), utána negatív (1)
0 1 1
1 0 0
1 0 0
1 0 0
0 1 1
1 0 1
0 1 0
0 1 0
not +1 neg
az átvitel most is terjedhet
Tekinthetjük úgy is, hogy az n biten ábrázolt számok a modulo 2n maradékosztályokat adják ki: az eljel nélküliek a 0..2n-1 reprezentánsokkal, az eljelesek a -2n-1..2n -1-1 reprezentánsokkal. Eljeles számok összeadását a nemnegatív számokhoz hasonlóan végezhetjük el. Kivonáshoz adjuk hozzá a számhoz az összeadandó negáltját. Törtszámok A törtszámok ábrázolását, amely általában ún. lebegpontos formátumban történik, és az ezek kezelésére való utasításokat nem mutatjuk be részletesen a munkafüzetben. A lebegpontos számok ábrázolását a numerikus analízis tárgyalja.
Kitlei Róbert
Assembly programozás
5
Karakterek, stringek Minden egyéb adathoz hasonlóan a karaktereket is bitekkel kell kódolnunk. Elször is rögzítenünk kell, milyen karaktereket szeretnénk ábrázolni: ez a karakterkészlet. Minden karakternek adunk egy sorszámot, ez a karakter kódja. Ahhoz, hogy különböz számítógépeken is ugyanazt a tartalmat kódolja ugyanaz a szöveg, meg kell állapodnunk a karakterek kódolásában: melyik karaktert milyen bitsorozattal ábrázoljuk. Egyszerbb a helyzetünk, ha minden karakter kódja azonos számú bitet tartalmaz (pl. nyolc bites ASCII, UCS-2), de helytakarékossági okokból lehet változó bithosszon is kódolni (pl. UTF-8). Minden esetben követelmény azonban, hogy az ábrázolt formából egyértelmen meg lehessen állapítani, mi a karakter kódja, abból pedig azt, hogy melyik karakterrl van szó. A számítástechnika korai éveiben nem volt szabványosítva, hogy a karaktereket hogyan kell kódolni, ezért a különböz architektúrájú gépek között nehéz volt adatcserét bonyolítani. Ezen sokat segített az 1967-ben megjelent ASCII kódolás, ami 7 bitben rögzítette a karakterkódok hosszát. Az ASCII karakterkészlete tartalmazza a számjegyeket, a latin kis- és nagybetket, vezérlkaraktereket, pl. sorvége, cseng, valamint különböz írásjeleket, pl. szóköz, pont, kérdjel, aláhúzás. Késbb kiderült, hogy a felhasználóknak szüksége van a fentieken kívül a saját nyelvükben elforduló, a latintól eltér karakterekre is, ezért többféleképpen kiegészítették 8 bitesre: az újonnan nyert pozíciókon az egyes régiók karakterei kerültek, pl. az ISO-8859-1 a nyugat-európai, az ISO-8859-2 a közép- és kelet-európai nyelvek (köztük a magyar) speciális karaktereit tartalmazza. Ez jópár évig elégnek tnt, azonban több probléma is adódott. Egyrészt ezek a karakterkódolások csak korlátozottan tették lehetvé, hogy többféle nyelvet lehessen egy dokumentumon belül használni. Amennyiben nem volt feltüntetve, melyik kódolást alkalmazza a dokumentum, téves választás esetén egyes karakterek helytelenül jelentek meg. További hiányossága volt ennek a rendszernek, hogy a lefedett nyelvek még mindig meglehetsen kevesen voltak: a távol-keleti nyelveket nem támogatta egyik kiterjesztés sem, ezekhez ismét más kódrendszerek születtek. A jelenlegi legátfogóbb karakterkódolási szabvány az 1991 óta létez Unicode, illetve ISO/IEC 10646. A kett számunkra nem számottev különbségektl eltéren megegyezik: a karakterkészlet és a kódolás ugyanaz a két rendszerben. Ez a karakterkészlet arra törekszik, hogy a világ összes létez és volt karakterét ábrázolni lehessen vele, azonban fenntartja a kompatibilitást az ASCII ISO-8859-1-es kiterjesztésével is, melyet a Unicode teljesen tartalmaz. Az Unicode-hoz többfajta kódolás létezik: a legismertebb a változó kódhosszú UTF-8. Ennek megvan az az elnye, hogy ha olyan szöveget kódolunk el vele, amely csak legfeljebb 127-es kódú ASCII karaktereket tartalmaz, akkor a kapott szöveg megegyezik az ASCII kódolással kapott szöveggel. Jelenleg a szabvány 5.0-ás verziója a legfrissebb, és folyamatosan fejlesztik. karakterkód 0 10 13 00 0A 0D vezérlkarakter szöveg vége új sor kocsi vissza karakterkód 32 48..57 65..90 97..122 20 30..39 41..5A 61..7A karakter szóköz 0 .. 9 A .. Z2 a .. z
ASCII szövegekben a sorvégét Unix rendszereken 0xA, Internetes protokollokban és Windows alatt 0xD 0xA jelzi; a Unicode szabványban mindkett, st, más kombinációk is jelezhetik a sor végét. Szövegek kódolásánál a karakterek kódjai sorban következnek egymás után. Kétféle konvenció van arra nézve, meddig tart a szöveg: a ,,C konvenció" szerint az els 0 kódú karakterig, a ,,Pascal konvenció" szerint pedig a szöveg kezdete eltt el van tárolva a hossza is.
2 Egyéb kódolásokban (pl. az IBM nagygépeken használatos EBCDIC) nem garantált, hogy a karakterek kódjai hézagtalanul követik egymást.
6
Assembly programozás
Kitlei Róbert
Feladatok 1. Mekkora értékek tárolhatóak egy bitvektorban? Add meg általánosan is! bitek száma számrendszer 8 16 32 4·n hexadecimális decimális hexadecimális decimális hexadecimális decimális hexadecimális decimális eljel nélkül eljelesen
legkisebb érték legnagyobb érték legkisebb érték legnagyobb érték
2. Töltsd ki az üres cellákat a táblázatban! bitek száma 8 8 8 16 16 16 3. Végezd el az alábbi mveleteket! 1001 1101 and 1100 0111 0110 1110 or 1000 0000 0101 1010 xor 1111 0001 -11 721
1001 0110 0001 1110 20 A5
bináris
1100 1111
decimális eljel nélküli eljeles
hexadecimális
6B
-24
0101 0110 1101 11012 + 1C E316 = ___________________2 = ______10 = _____16 16 53710 0000 0011 1010 11002 = ___________________2 = ______10 = _____16 54 3216 or 38 52910 = ___________________2 = ______10 = _____16 4. Írd le a saját nevedet ASCII kódolással! 5. Írd le a ,,Hello Vilag" szöveget ASCII kódolással!
Kitlei Róbert
Assembly programozás
7
Az architektúra alapjai A memória szervezése A memória bitekbl épül fel, de a biteket nem tudjuk külön-külön elérni: a legkisebb egység a nyolc bit együttesébl felépül oktet, amit általában byte-nak neveznek. A byte-ok sorban helyezkednek el a memóriában, mindegyik rendelkezik egy 32 bites sorszámmal, amit a byte memóriacímének nevezünk. Ez a lineáris memóriamodell; a hardver és az operációs rendszer elfedi ellünk a bonyolultabb szegmentált modellt. A processzor és a memória közötti adatforgalom három f vezetékköteg segítségével zajlik, amelyeket síneknek vagy buszoknak nevezünk. A memóriához való hozzáférés menete vázlatosan: a processzor elször elküldi a címsínen, hányas sorszámú cím tartalmához fogunk hozzáférni, és a vezérlsínen (I/O buszon) a járulékos információkat, például az adatforgalom irányát3 és az átvitelre váró adat méretét, ami lehet byte, szó (word, 2 byte) vagy duplaszó (dword, double word, 4 byte). Ezután maga az adatátvitel következik az adatsínen. Mivel a vezetékek egyszerre csak egy bitnyi információt tudnak szállítani, és azt is csak egy irányban, a processzor kommunikációs képességeit behatárolja, hogy az egyes sínek hány vezetéket tartalmaznak. Az adatsín szélességét az architektúra gépi szóhosszának hívják. A processzoron belül, a regiszterekben tárolható adatok méretét bels szóhossznak nevezzük. Az x86 architektúrán belül a 32 bites szervezés gépek gépi és bels szóhossza, valamint a címsín szélessége egyaránt 32 bit. Az architektúra korai tagjaiban a gépi szó még 16 bites volt, a ,,szó" ebbl a történelmi okból maradt meg 386-ostól felfelé ekkorának. Tehát a 32 bites processzorokon a gépi szó a duplaszó, és ha tehetjük, akkor ekkora méret adatokkal dolgozunk.4 Az architektúra egy, a programozó számára kevéssé intuitív tulajdonsága, hogy a byte-nál nagyobb méret adatok byte-jait ún. little endian sorrendben tárolja. Ez azt jelenti, hogy a legkisebb helyi érték byte kerül a legkisebb címre, az alulról következ byte a soron következ címre stb. Ez azt jelenti, hogy a szám leírt alakjához képest, ha a memóriát balról jobbra növeked címeken képzeljük el, éppen fordított sorrendbe kerülnek az adatok. Más architektúrák éppen ellenkezleg, big endian sorrendben írnak a memóriába (ekkor a helyi értékek csökkennek a címek növekedtével). Egyes architektúrákon átprogramozható futás közben, melyik módon mködjenek. Mivel a memóriacímek is csak számok, ezért a memóriában magában is el tudunk tárolni egy másik memóriacímet. Amikor egy 32 bites adatot nem pusztán számként, hanem memóriacímként értelmezünk, akkor az adatot mutatónak (pointernek) nevezzük, és ezek segítségével építhetek fel láncolt adatszerkezetek.
C15AEF4C C15AEF4D C15AEF4E C15AEF4F
a byte memóriacíme ... byte értéke (bináris) byte értéke (hexa)
Példa
...
0101 0000 1110 1111 0101 1010 1100 0001 50 EF 5A C1
Little endian: a fenti C15AEF4C címen található duplaszó értéke nem 50EF5AC1, hanem C15AEF50. Mutató: ha mutatóként értelmezzük a duplaszót, akkor az utána következ byte-ra mutat. Az adatok az adatszegmensben helyezkednek el, aminek a kezdetét a section .data direktíva vezet be. Adatot úgy lehet elhelyezni, hogy elször leírjuk, mekkora méret adatokkal foglalkozunk, majd leírjuk magukat az adatokat vesszkkel elválasztva. A lehetséges adatméretek: db, dw és dd, sorra byte, szó és duplaszó méret adatokat jelentenek. Az elhelyezett adatokat érdemes címkével is ellátni, mert ez megkönnyíti késbb a hozzáférést. A címkét a sorban csak szóközök és tabulátorok elzhetik meg; a címke neve tartalmazhat karaktereket, számjegyeket (számjeggyel nem kezddhet) és pontot. A címke végére lehet egy kettspontot tenni, de ez nem része a címke nevének. Amennyiben sok hasonló adatot szeretnénk elhelyezni, az adatméret jelzése elé írhatunk egy times mennyiség eltagot (a mennyiség egy konstans, pl. 15), ami a megadott mennyiséget helyez le az adatokból.
3 processzor memória vagy memória processzor 4 néhány éve megjelentek a legújabb, 64 bites processzorok is, amelyeken a gépi szó mérete 64 bit
8
Assembly programozás
Kitlei Róbert
section .data cimke Példa dd 1, 2, 0AF4B551Ch times 4 db 1 ; adat elhelyezése külön címke nélkül db 1, 1, 1, 1 ; hatása megegyezik az elz soréval: négy byte-nyi egyes szoveg db "ASCII szoveg", 0xA, 0 A fentiek hatására az alábbi byte-ok keletkeznek. Aláhúzás jelöli az egy egységként definiált byte-okat. 01 00 00 00 02 00 00 00 1C 55 4B AF 01 01 01 01 01 01 01 01 41 53 43 49 49 20 73 7A 6F 76 65 67 0A 00 Sokszor elfordul, hogy tudjuk, szükségünk lesz valamekkora adatterületre, de annak tartalmát kezdetben még nem töltjük fel. Ezek számára az inicializálatlan adatszegmensben tarthatunk fenn helyet. Ennek a szegmensnek a kezdetét a section .bss direktíva jelzi. Itt nem helyezünk el adatokat, csak tárterületet tartunk fenn számukra: resb mennyiség, resw mennyiség, resd mennyiség leírásával jelezhetjük, hogy az aktuális pozíciótól kezdden adott mennyiség byte-ra (resw esetén kétszer, resd esetén négyszer annyira) lesz szükségünk. Itt szintén érdemes címkéket alkalmazni. A programkód írása során az inicializált és inicializálatlan adatszegmensbe tartozó címkék használata nem különbözik. Lényeges különbség viszont, hogy a program futásának a kezdetén ezeknek a területeknek a tartalma nem ismert, és amíg nem töltöttük fel, ismeretlen értéket (társzemetet) tartalmaznak. section .bss Példa tomb resb 1024 fajlleiro resd 1 ; 1024 inicializálatlan byte lefoglalása, a kezdetének címkéje tomb ; a fajlleiro címkén 4 byte érhet el
A nasm címkéi kétfajták lehetnek. A globális címkék karakterrel kezddnek. A lokális címkék ponttal kezddnek, és a teljes alakjukat úgy kaphatjuk meg, ha hozzáfzzük a lokális nevet az eltte utoljára definiált globális címke nevéhez. Ez azért hasznos, mert ugyanazt a nevet újra fel lehet használni: globális nevet általában az alprogramok belépési pontjai és az adatszerkezetek szoktak kapni, lokális nevet az alprogramokban elforduló vezérlési szerkezeteknek és az adatszerkezetek komponenseinek adunk. Amíg egy globális címke hatókörében vagyunk, az alatta definiált lokális címkére a lokális névvel lehet hivatkozni, azonban egy másik globális címke hatókörében ki kell írni a lokális címke teljes nevét ámbár ha más globális címke alól szeretnénk a címkére hivatkozni, akkor érdemes megfontolni, hogy globális nevet adjunk neki. globalisCimke .lokalisCimke Példa ; teljes alakja: globalisCimke.lokalisCimke
Fontos: a memória nem ,,tud" arról, hogy mi hogyan szeretnénk értelmezni a tartalmát, nekünk kell arra ügyelnünk, hogy mindig megfelelen kezeljük azt. Ha pl. egy adatelem méretét dw adja meg, majd megpróbálunk belle byte hosszan olvasni, akkor nem kapunk hibát, hanem sikeresen kiolvassuk az ott elhelyezked szó alsó felét (mivel a little endian tárolás miatt az kerül az els byte-ra).
Kitlei Róbert
Assembly programozás
9
A regiszterek A legtöbbet használt adattárolók a regiszterek. Ezek a memória kiemelt szereppel rendelkez részei, melyek a memória többi részétl elkülönülnek mind fizikailag (a processzor belsejében helyezkednek el, és ezért nagyon gyorsan elérhetek), mind kezelésüket tekintve. ax, bx, cx, dx, sp, bp, si, di ah, bh, ch, dh al, bl, cl, dl
31. bit ................................................................................... 16. bit 15. bit ............................... 8. bit 7. bit ................................. 0. bit
0 1 1 0 0 0 0 1 1 1 0 1 1 0 1 1 1 1 1 1 0 1 1 0 1 1 0 0 0 0 0 0 eax, ebx, ecx, edx, esi, edi, esp, ebp a regiszterek részeinek nevei A programozás során nyolc általános célú regisztert használhatunk. Ezek 32 bitesek, neveik eax, ebx, ecx, edx, esi, edi, esp és ebp. Mindegyiknek elérhet külön regiszterként az alsó fele (a 15..0-adik bitekbl álló részregiszter): ax, bx, cx, dx, si, di, sp és bp. Ezek közül az els négy regiszter fels (15..8-adik bit) és alsó fele (7..0-adik bit) is egy-egy névvel ellátott, 8 bites regiszter: ah, al, bh, bl, ch, cl, dh, dl5. A regiszterek közül leggyakrabban a 32 biteseket fogjuk használni, és esetenként, például karakteres adatok kezelésére, a 8 biteseket. Van két olyan regiszter, amelyet nem lehet közvetlenül elérni, azonban a számítógép mködéséhez elengedhetetlen a létük. Az utasításmutató, eip (instruction pointer), azt mutatja, hogy melyik memóriacímrl folytatódik a végrehajtás, vagyis a memória melyik címérl kell a processzornak felolvasnia a következ utasítás kódját. A tartalma automatikusan változik, általában a közvetlenül az elz végrehajtott utasítás utáni címre mutat, kivéve az ugró utasítások esetén, amikor az ugrás céljának címét kapja értékül. A jelzbitek regisztere (eflags) az utoljára elvégzett számításról, illetve a processzor aktuális állapotáról tárol információkat.6 Bitjei közül az alábbiak változhatnak meg aritmetikai mveletek eredménye szerint. Jel Z Név zero Mikor egy az értéke? Jel S sign Név eljel Mikor egy az értéke? A szám negatív eljelesen.
nulla Az eredmény nulla.
C carry átvitel Eljel nélküli (túl-/alul-)csordulás. O overflow túlcsordulás Eljeles (túl-/alul-)csordulás. P parity paritás Az eredmény páros. Az utasítások szerkezete Az utasítások a gép mködésének elemi egységei: vezérlik a processzort, milyen mveletet végezzen az adatokkal, illetve melyik utasítással folytassa a végrehajtást. Felépítésüket tekintve egy mnemoniknak nevezett kulcsszóból és megfelel számú, vesszvel elválasztott operandusból (paraméterbl) állnak. Az operandusok számát a mnemonik után alsó indexben jelöljük az utasítások bemutatása során. Egy sorba legfeljebb egyetlen utasítás írható. Az utasítások a kódszegmensben találhatóak, ennek a kezdetét a section .text direktíva jelzi. A kétoperandusú utasítások, ha az utasítás leírása nem mond mást, az általuk elvégzett mvelet eredményét az els operandusban tárolják el, ezért arra célként, a második operandusra pedig forrásként fogunk hivatkozni. Az operandusok lehetséges kombinációi a munkafüzet végén lev táblázatban vannak összefoglalva. Két kivételtl (movsx, movzx) eltekintve teljesül továbbá minden ismertetett kétoperandusú utasításra, hogy a két operandus hosszának meg kell egyeznie: mindkettnek vagy byte-osnak, vagy szavasnak, vagy duplaszavasnak kell lennie.
5 Az 'e' bet a regiszterek elején azt jelöli, hogy ezek a 16 bites regiszterek kiterjesztett (extended) változatai, mert a számítógépcsalád els gépein még csak a 16 bites regiszterek voltak megtalálhatóak. A 'h' és 'l' a ,,fels" és ,,alsó" (high és low) szavak rövidítései. 6 Egyes architektúrákon (pl. IBM 360) az utasításmutató regiszter is tartalmaz jelzbiteket.
10
Assembly programozás
Kitlei Róbert
Konstansok Szám konstans leírásakor el kell döntenünk, milyen számrendszerben ábrázoljuk. · Ha decimális konstansról van szó, nem kell külön jelölést alkalmaznunk, csak leírjuk a számot: 157. · Bináris konstans esetén egy 'b' bett írunk a szám végére: 001011101b. · Hexadecimális konstansokat kétféleképpen tudunk írni. · A '0x' prefix után írjuk le a konstanst: 0xABCDE. · A konstans után 'h' posztfixet illesztünk: 8CDh. · Ha a szám betvel kezddik, nulla prefixet kell írnunk a szám elé: 0F352h. Enélkül ugyanis elfordulhat, hogy valamelyik regiszter, például ah nevével ütközik a leírt alak. Karakterstring konstansokat aposztrófok (') vagy idézjelek ('') közé írhatunk. Ezek értéke megegyezik azzal a bináris száméval, amit úgy kapunk, hogy a karakterek nyolc bites ASCII kódjait összefzzük. Itt, a numerikus értékekkel ellentétben, nem számít, hogy little vagy big endian üzemmódú-e a számítógép, mindig abban a sorrendben tárolódnak a byte-ok, ahogy a stringben szerepelnek. Lehetség van arra, hogy a konstansokból kifejezéseket alkossunk, amelyeket a nasm assembler fordítási idben, 32 biten ábrázolva kiértékel. A következ operátorok állnak a rendelkezésünkre, csökken precedencia-sorrendben. Fontos: ez csak konstansokra vonatkozik; regisztereket és memóriatartalmakat tartalmazó kifejezéseket csak utasításokkal tudunk kiszámítani. zárójelek bitenkénti negáció negatív eljel szorzás összeadás, kivonás bitenkénti eltolás bitenkénti és, vagy, kizáró vagy () ~ * + << >> & | ^
A konstans hosszának értelmezésében szerepe lehet annak is, hogy mi a másik paraméter: mov eax, 4 ugyanazt jelenti, mint mov eax, 0x00000004, azaz eax teljes tartalmát átírja, a második bit 1 lesz, a többi 0.
mov mov mov ecx, dl, al, 183 mov 101101b mov 'a' mov esi, ah, ax, 0x554C 0Ah 5+8/4
A memória címzése A memória tartalmának hozzáférésére sokféle módszer közül válogathatunk. Általánosan igaz mindegyiknél, hogy azt a címet, ahol az adatelem elkezddik a memóriában, szögletes zárójelek közé írjuk; ezt az alakot memóriatartalomnak nevezzük. A memória-hozzáférésnél fontos, hogy meg kell adnunk azt is, milyen hosszúságú adattal dolgozunk, mert önmagában a cím csak azt mutatja meg, hol kezddik a memóriában az adat. Ezt a byte, word vagy dword kulcsszavakkal lehet megadni. A legegyszerbb címzési mód, ha ismerjük a konkrét memóriacímet, ahová írni szeretnénk, ekkor egyszeren leírjuk szögletes zárójelek között, elé pedig az adat hosszát: word [11101b] vagy dword [0x1234ABCD]. Ha egy utasítás egyik operandusáról egyértelmen kiderül annak hossza a másik operandus alapján, a hosszinformáció elhagyható, különben nem. Nem egyértelm a következ utasítás. mov [1101011b], 18 A második operandusról nem derül ki, hogy byte, szó vagy duplaszó hosszan értelmezend-e a 18 konstans. Meg kell adni az operandus hosszát. mov [1101011b], dword 18 Az operandus hosszát megadhatjuk így is. mov dword [1101011b], 18 Egyoperandusú utasításnál nincs lehetség ilyen rövidítésre. Ekkor ki kell írni az operandus hosszát. inc [794h] inc dword [794h] mul [0B11h] mul byte [0B11h] Hibás
Példa
mov mov
eax, cl,
ebx + 5 Eh
mov
dl,
[adat] / 2
Hibás Hibás 11
Kitlei Róbert
Assembly programozás
Általában nem ismerjük számszerleg a memóriacímet, mert kézzel kiszámolni nehézkes lenne. Ennek megkönnyítésére lehetség van a memória egyes pontjaihoz címkéket hozzárendelni. Címkét definiálni úgy lehet, hogy a forráskódban a sor elejére leírjuk a címke nevét. Ha a forráskód egy pontjain leírjuk a címke nevét, az ekvivalens azzal, mintha azt a memóriacímet írtuk volna le konstansként, ahol a címkét definiáltuk. Természetesen két különböz címke nem kaphatja ugyanazt a nevet. A szögletes zárójelen belül az alább felsoroltak közül egy vagy több összetev összege állhat. · egy 32 bites regiszter · egy skálázott (konstans szorzóval ellátott) 32 bites regiszter, a skálatényez 1, 2, 4 vagy 8 lehet · egy 32 bites konstans7 mov mov mov mov eax, eax, esi, eax, [cimke] [ebx] [esi] [ebp-4] Példa mov mov mov al, [ebx+esi] mov [1101011b], 18 eax, [edx+4*edi] mov [eax-ecx], edx edx, [adat+8] mov eax, [[esi+8]+4] inc [794h] mul [0B11h] mov edi, [al] Hibás
Adatmozgatás Alapvet igény, hogy adatokat mozgassunk. Ezt a mov2 utasítással lehet elérni. Hatására a céloperandus felveszi a forrás értékét. Lehetség van arra is, hogy kisebb méret adat kiterjesztésével töltsünk fel regisztert vagy memóriatartalmat. Ezt tehetjük eljel nélkül: movzx2 (ekkor nullákkal töltdik fel a bvebb fels rész), vagy eljelesen: movsx2 (a kisebb méret adat legfels bitjének értékével töltdik fel a maradék). Végrehajtásuk után a cél értéke megegyezik a forrás értékével eljel nélkül, illetve eljelesen. Amennyiben meg akarjuk cserélni a forrást és a célt, az xchg2 utasítást használhatjuk. Ennek az utasításnak egyik operandusa sem lehet konstans. Logikai utasítások A logikai utasítások: not1, and2, or2, xor2 (negáció, és, vagy, kizáró vagy) bitenként valósítják meg a fent leírt logikai mveleteket az operandusokon. Az and segítségével törölni (nullára állítani) lehet kiválasztott biteket: olyan konstanst kell második operandusnak adni, amely pontosan a törlend pozíciókon tartalmaz nullát, a többin egyest. Az or-ral éppen fordítva, beállítani lehet biteket, ha a konstans beállítandó pozícióin egyest, a többin nullát tartalmaz. Az xor pedig megfordítja azokat a biteket, amelyek egyesre vannak állítva. Regisztert úgy is törölhetünk, hogy xor-oljuk a regisztert önmagával. Ez pontosan azokat a biteket fordítja meg a regiszterben, amelyek be vannak állítva benne, vagyis az eredmény valóban nulla. A setfeltétel1 utasítások egyetlen, byte méret operandusukat 1-re állítják be, ha teljesül a megadott feltétel, 0-ra, ha nem. A feltételek lehetséges alakjait a feltételes ugrásoknál írjuk le részletesen; ezeknek az utasításoknak az alakja annyiban tér el a feltételes ugró utasításokétól, hogy 'j' helyett 'set' a mnemonik eleje. Bitléptet és bitforgató utasítások Ezek az utasítások (sar2 és shl2, sal2, shr2, rol2, ror2, rcr2, rcl2) kétoperandusúak, a második paraméterük konstans vagy a cl regiszter lehet. A mködésük szerint a biteket léptetik annyi pozícióval, amennyit a második operandus megszab. A léptetés iránya a mnemonik utolsó betjétl függ: ha 'l', felfelé (left, azaz balra), ha 'r', lefelé (right, jobbra). Ha az els bet 'r' (rotate), akkor forgatásról van szó, vagyis a regiszter végén túlcsorduló, kilép bitek a másik oldalról jönnek be. Az sar utasítás esetén a legfels bit értéke forgatódik be felülrl, az összes többi léptet utasításnál 0 bitek lépnek be. Ketthatvánnyal való szorzást elvégezhetünk léptet mveletek segítségével: eljel nélküli szorzásra és osztásra az shl és shr, eljeles szorzásra és osztásra a sal és sar utasítások használhatóak. Az operandus azt adja meg, kett hányadik hatványával szorzunk/osztunk. Könnyebb megértéshez lásd a munkafüzet végén szerepl ábrákat.
7 Konstanst látszólag ki lehet vonni, ez azonban valójában egy modulo 232 összeadásként jelenik meg.
12
Assembly programozás
Kitlei Róbert
Aritmetikai utasítások Az add2 és a sub2 segítségével lehet összeadni és kivonni. Mindkett mködik eljeles és eljel nélküli számokra. Az eggyel való növelés és csökkentés annyira gyakori, hogy erre külön van egy-egy utasítás: inc1 és dec1. Szorozni a mul1 egyoperandusú utasítással lehet eljel nélkül, az imul1 segítségével pedig eljelesen. Ezek egy regisztert vagy memóriatartalmat kapnak; az operandusuk nem lehet konstans. Az operandus értékével megszorozzák eax-nak az operandus hosszával megegyez méret részét (al, ax vagy eax) és a szorzatot elhelyezik ax-ben, dx:ax-ben vagy edx:eax-ben. Ez utóbbi kett azt jelenti, hogy az eredmény fels fele kerül (e)dx-be, az alsó fele pedig (e)ax-be; a két regisztert átmenetileg egy nagyobb regiszter két felének képzelhetjük el. Erre azért van szükség, mert a szorzat közel kétszer akkora helyet is igényelhet, mint a tényezk. mov mov mul eax, 4141659 ebx, 2356781 ebx mov eax, 0xA7F04BFF ; ekvivalens eredményt ad ezzel mov ebx, 2356781 ; mert 414659 · 2356781 = mov edx, 8E0h ; 8E016 · 232 + A7F04BFF16 Példa
Osztani a div1 és az idiv1 szintén egyoperandusú utasításokkal lehet. Ezeknél a forrás és a cél éppen fordítva van a szorzásokhoz képest (itt is a cél méretétl függen). Továbbá az osztás maradékát is megkapjuk, az operandus méretétl függen edx-ben, dx-ben illetve ah-ban. Értéket negálni a neg1 utasítással lehet. A negáció mveletének menetét lásd az eljeles számokat leíró részben. Vezérlésátadás Elfordul, hogy a program végrehajtását egy másik címen szeretnénk folytatni. Egy ilyen eset, amikor végrehajtottuk egy elágazás igaz ágát, és a hamis ágat, aminek a kódja közvetlenül az igaz ág kódja után következik, már nem akarjuk végrehajtani. Ilyenkor az igaz ág kódjának a végére elhelyezünk egy feltétel nélküli ugrást, aminek hatására a vezérlés a hamis ág kódja után folytatódik, vagyis a teljes elágazás kódja után. A feltétel nélküli ugrás jmp1, operandusa pedig az a címke, ahová az ugrás után a vezérlést juttatni szeretnénk. Vannak olyan esetek, amikor valamilyen feltételtl függen szeretnénk csak átadni a vezérlést máshová, ha pedig nem teljesül a feltétel, a következ utasítást akarjuk futtatni. Az elbbi példánál maradva, az elágazás feltételének vizsgálatakor pontosan ez a helyzet: a teljesül a feltétel, az igaz ág kódját hajtjuk végre, ami közvetlenül az ugrás után áll, ha nem teljesül a feltétel, akkor pedig el kell ugranunk a hamis ágra. A feltétel megvizsgálását a cmp2, a feltételes ugrásokat pedig a jfeltétel1 utasításokkal tudjuk megvalósítani. Ezeknek az alakja: a 'j' (ugrás, jump) után opcionálisan egy 'n' (nem, not), aztán az 'a', 'b', 'c', 'g', 'l' közül valamelyik (felett, alatt, átvitel, nagyobb, kisebb: above, below, carry, greater, less), majd opcionálisan egy 'e' (egyenl, equal); lehetséges alakok még önmagukban a je és jne. A cmp szerepe, hogy a jelzbitek regiszterét beállítja a feltételes ugrás végrehajtásához szükséges értékekre, de a paraméterek tartalmát nem változtatja meg. A feltételes ugrások megvizsgálják a jelzbitek állását, majd ennek eredménye szerint ugranak az operandusban kapott címre, vagy folytatják a végrehajtást. A feltételek a következek lehetnek. · e ugrik, ha az t megelz cmp két paraméterének az értéke azonos · a és b ugrik, ha a cmp els paraméterének az értéke eljel nélkül nagyobb (b: kisebb) · g és l ugrik, ha a cmp els paraméterének az értéke eljelesen nagyobb (l: kisebb) Az 'e' bet megengedi az egyenlséget is, az 'n' bet a feltétel tagadása. A feltétel nélküli ugrások tetszlegesen távoli kódra át tudják adni a vezérlést. A feltételes ugrások alapesetben csak ,,közelre" tudnak ugrani, ezért célszer a near kulcsszót használni a feltételes ugrás mnemonikja után. Példa cmp jnbe near eax, ebx címke ; akkor ugrik, ha eax eljel nélkül összehasonlítva ; szigorúan nagyobb (nem kisebb vagy egyenl), mint ebx
Az alprogramhívás eszköze a call1 és a ret0, a kivételkezelésé az int1, melyeket a megfelel helyeken ismertetünk.
Kitlei Róbert
Assembly programozás
13
Programozási konstrukciók megvalósítása Változók deklarációja, definíciója Amikor egy változóval dolgozunk egy programozási nyelven, mindig tudnunk kell, hogy mekkora területet foglal, illetve milyen mveleteket hajthatunk végre rajta. Magasszint programnyelveken ezek az információk a deklarációkból derülnek ki, így a fordítóprogram akkor is képes a megfelel kódot generálni az adat elérésére, ha az egy másik fordítási egységben helyezkedik el. Az assembly szintjérl nézve az adatok pusztán byte-sorozatok. Itt a hosszra és az elérésre vonatkozó információkat közvetlenül a generált kódban kell helyesen felhasználni. Ehhez fordítóprogram írása során fel kell használni a rendelkezésre álló adatokat, forráskód közvetlen írása során pedig ügyelni kell, hogy ne helyesen írjuk le az adathozzáférés méretét, például duplaszavasan tárolt adatot ne próbáljunk nyolcbites regiszterbe tölteni. Értékadás A mov (ha szükséges: movsx, movzx) utasítás segítségével lehet értéket adni egy memóriatartalomnak. Ha az adat hossza nagyobb egy gépi szónál, akkor több mozgatásra van szükség. Ehhez az adatot fel kell bontani legfeljebb duplaszó hosszúságú részekre, és külön kell értéket adni a részeknek. A mov nem tud két memóriatartalmat operandusként fogadni, hiszen az adatsínen nem tudunk egyszerre befelé és kifelé is adatot mozgatni, a két lépés közti tárolásra az egyik általános célú regisztert használhatjuk. A v1 és v2 változók 8 byte hosszon tartalmaznak adatokat. Valósítsuk meg a v1 := v2 értékadást! Példa mov mov mov mov eax, [v2], eax, [v2 + 4], [v1] eax [v1 + 4] eax ; az els 4 byte felolvasása ; az els 4 byte kiírása ; a második 4 byte felolvasása ; a második 4 byte kiírása
Goto Ez a konstrukció szinte minden programnyelven majdnem ugyanúgy jelenik meg, mint assemblyben, nem véletlen, hogy jóval megelzte a strukturált programozást. Megvalósítani egy jmp utasítással lehet, amelynek operandusa a célcímke.
14
Assembly programozás
Kitlei Róbert
Kifejezések Magas szint programozási nyelveken könnyen lehet egyetlen sorba hosszú kifejezéseket írni, ezeket assemblyre fordítva azonban akár több képernynyi hosszúságú kódot is kaphatunk. A kifejezések átírását a következ módszerrel tehetjük meg. Ennek a módszernek az alkalmazásához szükség van a futási idej verem ismeretére is, lásd 25. oldal. Alkossuk meg a kifejezés szintaxisfáját, majd járjuk be a fát posztorder sorrendben. Ha egy konstanssal vagy memóriatartalommal találkozunk, akkor azt tegyük be a verembe. Ha mveleti jelhez érünk, annak paraméterei sorban a verem tetején találhatóak; vegyük ki ket a regiszterekbe, hajtsuk végre a mveletet a megfelel assembly utasítással, majd az eredményt tegyük ismét a verembe. Amikor a kifejezés végére értünk, a verem tetején a kifejezés értéke található. Komolyabb nyelv esetén a közbüls lépések során az eredmény vermelését típusellenrzés elzi meg, például történt-e túlcsordulás. A tömbök indexelése, lista következ elemére hivatkozás, és bármilyen olyan lépés, ami mutató vagy hivatkozás feloldását vonja maga után, szintén mveleti jelnek minsül, amit megvalósítani indirekcióval lehet. Természetesen sokkal hatékonyabb és kényelmesebb módon is ki lehet értékelni a kifejezéseket, amennyiben a verem helyett regisztereket használunk az átmeneti értékek tárolására. Amint az ember egy kis gyakorlatra tesz szert az assembly programozás terén, kézzel elég egyszer lesz olvashatóbb, rövidebb módon leírni egy kifejezés kiértékelését. Ugyanerre a fordítóprogramok fejlett technikákat, például gráfszínezést alkalmaznak. Az a, b, c, i és j változók 32 bitesek, d 32 bites értékeket tartalmazó tömb. Valósítsuk meg a j := ( a + b ) * ( c d[i] ) értékadást! push push pop pop add push push push push pop pop mov push pop pop sub push pop pop mul push a verem tartalma eax ebx dword [a] ;a dword [b] ;ab eax ;a b ebx ; b a eax, ebx ; a+b eax ; (a+b) dword [c] ; (a+b) c dword [d] ; (a+b) c d dword [i] ; (a+b) c d i eax ; (a+b) c d i ebx ; (a+b) c i d eax, [ebx+4*eax] ; (a+b) c d[i] eax ; (a+b) c d[i] eax ; (a+b) c d[i] ebx ; (a+b) d[i] c ebx, eax ; (a+b) c-d[i] ebx ; (a+b) (c-d[i]) eax ; (a+b) c-d[i] ebx ; c-d[i] (a+b) ebx ; feltételezzük, hogy a szorzat belefér eax-be eax ; a végeredmény a verem tetején van ugyanez a számítás rövidebb kóddal mov eax, [a] add eax, [b] ;a+b mov ebx, [c] mov ecx, [i] mov edx, [d] mov edx, [edx+4*ecx] ; d[i] sub ebx, edx ; c d[i] mul ebx ; a végeredmény eax-ben található
Példa
+ a b d [] i
Kitlei Róbert
Assembly programozás
15
Szekvencia Ez a legegyszerbb vezérlési szerkezet: csak le kell írni a programrészletek kódját egymás után. Elágazás Az elágazás minden ága számára tartsunk fenn, még csak gondolatban, egy (lokális) címkét, valamint az elágazás vége számára is egyet. Az elágazások mindig egy kifejezés kiértékelésével kezddnek; igaz-hamis elágazások esetén a kifejezés egy logikai értéket ad. Ezután sorban következnek az ágak kódjai. Az elágazás elején a feltétel vizsgálata a következképpen történik. Kiszámítjuk a feltétel értékét (ami egy logikai érték) egy változóba. Ezután megvizsgáljuk, hogy az elágazás melyik ágába kell átadnunk a vezérlést. Az értéket egy cmp és egy feltételes ugrás segítségével tudjuk összehasonlítani; többágú elágazás esetén több ilyen utasításpárt írunk le egymás után. Kihasználható, hogy a vezérlés a következ utasításon folytatódik, ha nem teljesül a feltétel, ezért egy kiválasztott ág kódja közvetlenül az elágazás után következhet. Gyakori eset, amikor azt kell megvizsgálnunk, hogy egy kifejezés értéke beleesik-e egy intervallumba. Ennek vizsgálatához, két cmp/jmp utasításpár szükséges: az els két utasítással vizsgáljuk meg, kisebb-e az alsó határnál, a második kettvel pedig azt, nagyobb-e a felsnél. Mivel sem a cmp, sem a feltételes ugrások nem változtatják meg a vizsgálandó értéket, a konstrukció természetes módon konjunkcióként viselkedik.8 Írjunk elágazást attól függen, hogy az a és a b változó értéke megegyezik-e. mov eax, [a] cmp eax, [b] jne .hamis.ag .igaz.ag ; erre a címkére nincsen feltétlenül szükség, a jobb olvashatóság kedvéért szerepel ; itt következik az igaz ág kódja jmp .elagazas.vege .hamis.ag ; itt következik a hamis ág kódja ; ide nem szükséges ugró utasítást elhelyezni, mert éppen az elágazás végénél vagyunk .elagazas.vege Példa 16
8 nem túl gyakran elfordul, hogy ennél több összehasonlításra van szükség, például ha EBCDIC kódolás szerint vizsgáljuk meg, kisbetrl van-e szó. Ezekben az esetekben is fel lehet bontani a feltételeket intervallumok uniójára; sorban vizsgáljuk meg, hogy az egyes intervallumok közül beleesik-e valamelyikbe az érték.
Assembly programozás
Kitlei Róbert
Rövidzáras operátorok Kétfajta logikai vagy és logikai és szokott a programokban szerepelni. Az ún. mohó operátorok mindkét részkifejezést kiszámítják, majd az eredményekbl meghatározzák a kifejezés eredményét a megfelel logikai operátorral. A lusta kiértékelés vagy más néven rövidzáras operátorok azonban, ha az els részkifejezésbl már rögtön megadható az egész kifejezés értéke, a második részkifejezést nem számítják ki. Egy lehetséges módszer mohó operátorok kiszámítására, hogy egyszer kifejezéseknek tekintjük ket, a feltételeket pedig a setfeltétel utasítások közül a megfelel segítségével állítjuk el. Rövidzáras operátoroknál a következ kódot írhatjuk. Ez több, azonos operátorból álló kifejezés-láncok kiszámítására alkalmas. konjunkciós lánc ; a két összehasonlítandó ; meghatározása eax-be és ebx-be cmp jfeltétel tagadása near eax, ebx .hamis.ag diszjunkciós lánc ; a két összehasonlítandó ; meghatározása eax-be és ebx-be cmp jfeltétel near eax, ebx .igaz.ag
; a fenti konstrukció ; alkalmazása a konjunkciós lánc minden elemére .igaz.ag jmp .vege .hamis.ag .vege ; itt szerepel az igaz ág kódja ; itt szerepel a hamis ág kódja
; a fenti konstrukció alkalmazása ; a diszjunkciós lánc minden elemére .hamis.ag jmp .vege .igaz.ag .vege ; itt szerepel a hamis ág kódja ; itt szerepel az igaz ág kódja
Valósítsuk meg az ( a < b ) && ( c - 1 < 2 * d ) Valósítsuk meg az ( a + b < 1 ) || ( c <= a & d ) logikai logikai feltétel kiértékelését rövidzáras operátorral! feltétel kiértékelését rövidzáras operátorral! mov mov cmp jnl near mov dec mov shl cmp jnl near eax, [a] ebx, [b] eax, ebx .hamis.ag eax, eax ebx, ebx, eax, .hamis.ag [c] [d] 1 ebx mov add cmp jl near mov mov and cmp jnle near eax, eax, eax, .igaz.ag eax, ebx, ebx, eax, .hamis.ag [a] [b] 1 [c] [a] [d] ebx ; az utolsó ág ; összevonható a ; jmp .hamis.ag utasítással ; (a feltétel megfordul)
Példa
.igaz.ag ... jmp .vege .hamis.ag ... .vege
Kitlei Róbert
.igaz.ag ... jmp .vege .hamis.ag ... .vege
Assembly programozás
17
Elöltesztels ciklus Elöltesztels ciklus írásához két lokális címkére lesz szükségünk. 1. Az els címke a ciklus kezdetét jelzi, ide fogunk a ciklusmag végrehajtása után visszaugrani. 2. Ezután következik a belépési feltétel kiértékelése. Ha nem teljesül a belépési feltétel, akkor elugrunk a második lokális címkénkre. 3. Ekkor következik a ciklusmag kódja. 4. A ciklusmag kódja után egy ugró utasítással átadjuk a vezérlést az els címkénkre. 5. Végül leírjuk a második címkét, amelyen a program tovább folytatódik. Tegyük fel, hogy van egy int i változónk, amelyet 32 biten, eljelesen ábrázolunk. while ( 5 < i || i < 11 ) { ++i; } A fenti C programrészletet a következképpen tudjuk megvalósítani. .ciklus.kezdete cmp dword [i], 5 jg near .ciklusmag cmp dword [i], 11 jl near .ciklusmag jmp .ciklus.vege .ciklusmag inc dword [i] jmp .ciklus.kezdete ; ; ; ; ; 1. 2. 2., 5 < i teljesül 2. 2., i < 11 teljesül
Példa
; ez a címke a vagy rövidzárassága miatt keletkezett ; 3. ; 4. ; 5.
.ciklus.vege
18
Assembly programozás
Kitlei Róbert
Hátultesztels ciklus A hátultesztels ciklus nagyon hasonló szerkezet az elöltesztels ciklushoz. Szintén két lokális címkére lesz szükségünk. 1. Az els címke a ciklus kezdetét jelzi, ide fogunk a ciklus végén visszaugrani. 2. Itt következik a ciklusmag kódja. 3. Ezután következik a folytatási feltétel kiértékelése. Ha teljesül a feltétel, akkor elugrunk a ciklus kezdetén elhelyezett lokális címkénkre, különben pedig a második címkére. A második címkére való ugrás egyszer feltétel esetén el is maradhat, hiszen ekkor a feltételes ugráson túlhaladva a program futása ugyanott folytatódik. 4. A második címkével jelezzük a ciklus végét. Példa: tegyük fel, hogy van két int típusú változónk, i és j, melyeket 32 biten, eljelesen ábrázolunk. do { --i; } while ( 19 < i + j && i < 11 ); A fenti C programrészletet a következképpen tudjuk megvalósítani. .ciklus.kezdete dec dword [i] Példa mov add cmp jg cmp jnl inc jmp eax, [i] eax, [j] eax, 19 .ciklus.kezdete dword [i], 11 .ciklus.vege dword [i] .ciklus.kezdete ; 1. ; 2. ; ; ; ; 3. 3. 3. 3., 19 < i + j teljesül
; 2. ; 2., i < 11 nem teljesül ; 3. ; 4. ; 5.
.ciklus.vege
Kitlei Róbert
Assembly programozás
19
Rögzített lépésszámú ciklus Amennyiben a ciklusmagot korlátos sokszor kell végrehajtani, a következ kódot generálhatjuk. Két lokális címkét fogunk elhelyezni a kódban. · Szükségünk lesz a ciklusszámláló eltárolására. Erre a célra egyszer ciklusok esetén fenntarthatjuk valamelyik regisztert, összetettebb ciklusok esetén pedig lefoglalhatunk egy memóriaterületet az adatszegmensben, vagy tárolhatjuk a ciklusszámlálót a futási idej veremben. · Miután eldöntöttük, hol tároljuk a ciklusszámlálót, töltsük fel a kezdeti értékét. · Írjuk le az els lokális címkét, amely a ciklus kezdetét jelzi. · Vizsgáljuk meg, hogy a ciklusszámláló túlhaladta-e már az értékhatárt. Ha igen, ugorjuk a második címkére. Amennyiben a ciklusszámláló még a határon belül van, a program futása folytatódik. · Írjuk le a ciklusmag kódját. A ciklusmagon belül felhasználhatjuk a ciklusszámláló értékét, de ne változtassuk meg. A ciklusmagon belül elhelyezhetünk kiugrást a ciklus végére. · Léptessük a ciklusszámlálót. · Egy feltétel nélküli ugrással irányítsuk vissza a vezérlést az els címkére, a ciklus kezdetére. Adott egy int n változó. Valósítsuk meg az alábbi programot assembly nyelven. for ( int i = 0; i < 15; ++i ) { if (n % 2 == 0) n = n / 2; else n = 3 * n + 1; if (1 == n) break; } mov eax, 14 ; eax regiszterben tároljuk az n változót mov ecx, 0 ; a ciklusszámláló inicializálása .ciklus.kezdete cmp ecx, 15 jnl .ciklus.vege mov and cmp jne edx, eax edx, 1 edx, 0 .paratlan
; a ciklusfeltétel vizsgálata ; a feltétel vizsgálatához csak az utolsó bitre van szükségünk
Példa
.paros shr eax, 1 jmp .elagazas.vege .paratlan mov edx, eax shl eax, 1 add eax, edx inc eax .elagazas.vege cmp eax, 1 je .ciklus.vege
; eljel nélküli, kettvel való osztás
; eljel nélküli, kettvel való szorzás ; ... és még egyszer az eredeti
; break
inc ecx ; a ciklusszámláló növelése jmp .ciklus.kezdete ; ugrás a ciklusmag elejére .ciklus.vege 20
Assembly programozás
Kitlei Róbert
Leszámláló ciklus A leszámláló ciklus olyan rögzített lépésszámú ciklus, amelyben a ciklusszámláló csökken, és a ciklusfeltételt a ciklus végén ellenrizzük. Ha nem végzünk külön ellenrzést eltte, akkor a cikusmag legalább egyszer lefut. Ügyelni kell arra, hogy a ciklusszámláló kezdetben ne legyen nulla, különben alulcsordulás miatt a ciklus szándékainkkal ellentétben nemhogy egyszer sem, de 232-szer fut le. Amennyiben nem alkalmazunk egyéb számlálót, figyelembe kell venni a ciklusmagban, hogy a ciklusszámláló felülrl lefele számol. Szerkezete nagyon egyszer. A ciklus kezdete eltt fel kell tölteni a ciklusszámlálót. Egyetlen címkére van szükségünk a ciklus elején. Utána közvetlenül a ciklus kódja következik, majd a ciklus végén alkalmazzuk a loop1 utasítást9. Ennek egy címke az operandusa, és a következ három utasítással ekvivalens mködés. Kötött, hogy ecx regisztert tekinti ciklusszámlálónak. dec ecx cmp ecx, 0 je loop_utasítás_operandusa_címke Számítsuk ki az els n szám összegét (n 1). int i = n; int osszeg = 0; do { osszeg += i; } while (--i); mov ecx, n mov eax, 0 .ciklus.kezdete add eax, ecx loop .ciklus.kezdete ; eax regiszterben tároljuk a ciklusszámlálót ; osszeg inicializálása
Példa
; a ciklusfeltétel vizsgálata
9 Természetesen a vele ekvivalens kódrészletet is leírhatjuk. Ennek további elnye lehet, hogy ekkor ecx helyett más regisztert is választhatunk ciklusszámlálónak.
Kitlei Róbert
Assembly programozás
21
Feladatok 1. Add meg a regiszterek tartalmát minden utasítás után! A feladatot a jelölt pontokon is el lehet kezdeni.
reg. vagy mem. a. mov ebx, 1023 mov al, -100 movsx eax, bl b. mov eax, 1C34F6E2h ebx al eax ax ah al c. mov byte [0FAh], 01Ch [0xFA] mov byte [0FBh], 0x22 [0xFB] mov ax, [0FAh] d. mov eax, 89ABCDEFh mov ah, al mov al, 42 mov bl, al mov bh, al e. mov ax, 0FF42h mov bx, 0DCBAh sub bh, al xor ax, bx f. mov al, 01011010b or ah, 10101010b g. mov al, 120 not al neg al h. mov bh, 254 inc bh add bh, 1 i. mov cx, 0x01F0b mov eax, -128 mul cx imul cl ax ax ah al bl bx ax bx bh ax bx al ah al al al bh bh bh cx eax dx : ax ax decimális bináris (megfelel bithosszon) eljel nélküli eljeles hexadecimális
2.
a. Az x, y és z címen byte-os adatok vannak. Számítsd ki z-be ( not x | y ) & x értékét! b. Számítsd ki eax-be ( eax 1 ) * 2 értékét!
22
Assembly programozás
Kitlei Róbert
3. Adj meg minél több utasítást, amely a. nullára állítja eax értékét, és a memória és minden egyéb regiszter tartalmát változatlanul hagyja! b. nem változtatja meg a regiszterek értékét (legfeljebb a jelzbitek regiszterének kivételével)! c. eax 2. bitjét egyesre állítja, a többit pedig változatlanul hagyja! d. az ellentétére állítja ebx legalsó (0.) és legfels (31.) bitjét! e. al tartalmát egy elre adott értékrl egy másik adott értékre állítja! (Például 229-rl 33-ra.) 4. Az x és y két címke, melyeken 32 bites adatokat tárolunk. a. Valósítsd meg az x := y értékadást! Közvetlenül egyik címrl a másikra másoló utasítás nincsen.10 b. Cseréld meg x és y tartalmát! 5. Milyen byte-okat tartalmaz sorban az adatszegmens? section .data db db db db dw dd 55h 55h, 56h 'a', 0Ah "hello" 1234h 89ABCDEFh
6. Valósítsd meg az ismertetett utasítások segítségével az adc eax, ebx utasítást! Az adc az els operandushoz hozzáadja a másodikat (mint az add), és még ehhez az átviteli bitet (azaz hozzáad még egyet, ha az átviteli bit be van állítva, különben az eredmény a két operandus összege). 7. Mennyi a kódrészletek végrehajtása után eax értéke? .a xor inc jmp dec mov .vege 8. Írj olyan kódrészletet, amely ecx-be meghatározza eax és ebx maximumát! A számok eljel nélkül vannak ábrázolva. 9. Lineáris keresés: az adat címkétl kiindulva keresd meg az els olyan byte-ot, amelyik nullát tartalmaz, és ennek a címét add meg eax-ben! 10. Valósítsd meg assembly nyelven az alábbi C programrészletet! Az a és b változók típusa int. for ( int i = 0; i < 10; ++i ) a += b; ebx, ebx ebx .vege ebx eax, ebx .b cmp eax, 0 je near .vege mov .vege inc eax, eax jmp eax .vege .c .c cmp eax, eax je near .vege dec eax
11. Az n paraméter a memóriában található egy duplaszavas változóban. Számítsd ki az eax regiszterbe a. az els n szám összegét! b. n! értékét!
10 Egy ilyen utasításnak egyszerre kellene a memóriabuszon beolvasnia és kiírnia az adatot, márpedig az csak egyirányú adatforgalomra képes.
Kitlei Róbert
Assembly programozás
23
12. Adott egy duplaszavas értékeket tartalmazó láncolt lista címke. A lista egy eleme egy duplaszavas mutatóból és a duplaszavas értékbl áll. A mutató a következ elemre mutat; ha nincs következ elem, nullát tartalmaz. Add meg a lista hosszát! 13. Adott a szoveg címkén egy szöveg, amelynek ismert a hossza. Fordítsd meg helyben! 14. Adott három idpont: egy-egy óra és perc, amelyek duplaszavasan vannak eltárolva a memóriában. Az els két idpont között részleges napfogyatkozás következik be. A napfogyatkozás mértéke a félidig lineárisan növekszik, félidben pontosan 50%, onnantól lineárisan csökken. Add meg, hogy a harmadik idpontban (amelyrl feltételezhet, hogy az elz kett között helyezkedik el) mekkora volt egész százalékra kerekítve a napfogyatkozás mértéke! 15. A teniszben az a játékos nyer meg egy ún. rövidített játékot, aki elször ér el legalább 7 pontot úgy, hogy legalább 2 ponttal vezet (azaz ha az állás 7-6, akkor még nem dlt el a rövidített játék, például a második játékos megszerezheti a következ három pontot, és akkor nyer 7-9-re). Egy tömbben adott, hogy melyik játékos nyerte meg a soron következ játékot. A tömb hossza elre nem ismert. Add meg, ki nyerte a rövidített játékot, és milyen arányban!
24
Assembly programozás
Kitlei Róbert
A futási idej verem szerepe: rekurzív alprogramhívások megvalósítása Az alprogramok (rutinok, szubrutinok) paraméterezhet, távolról meghívható programkód-részek. Két fajtájuk a függvények, melyek ezekbl visszatérési értéket számolnak ki, és az eljárások, melyek a paraméterként kapott mutatókon keresztül megváltoztathatják a memória tartalmát. Fleg a C alapú nyelvekben a két fogalom nem különül el, ezekben mindkettt függvénynek nevezik. A verem célja az, hogy egyszer módon lehessen kezelni az egymásba ágyazott alprogramhívások adatait. Akkor kap igazi jelentséget, ha a hívások rekurzívak, azaz az alprogram futása közben ismét meghívjuk az alprogramot (általában más paraméterezéssel, mint els alkalommal). Íme egy példa arra C nyelven, miért is van erre szükség.
int fakt( int n ) Hívjuk meg a függvényt fakt( 2 ) paraméterezéssel. { Ekkor a vezérlés átkerül a függvény kódjának az elejére. Mivel 2 0, az int v; eljárás hamis ágát hajtjuk végre. Itt ki kell számítanunk fakt( 1 ) * 2-t, ehhez if ( n == 0 )v = 1; pedig ismét meg kell hívnunk a függvényt, ezúttal fakt( 1 ) paraméterezéssel. else A függvény most két példányban fut: két, eltér paraméterezéssel. A ,,bels" faktoriális még egy példányt elindít a függvénybl, fakt( 0 ) hívással, v = fakt( n 1 ) * n; ami visszatér 1-gyel, majd ezt felhasználva fakt( 1 ) is visszatér 1-gyel, és csak return v; ekkor tud fakt( 2 ) is visszatérni, 2 * 1-el. }
Azokat a kódrészleteket, amelyek több példányban is futhatnak egyszerre, reentránsnak nevezzük. Azokat a memóriacímeket, ahová átadva a vezérlést elkezdhetjük a kódrészlet végrehajtását, a kód belépési pontjainak nevezzük. A programozó, illetve a fordítóprogram feladata garantálni, hogy a kódot csak ezeken a pontokon kezdjük el végrehajtani. A belépési pontokat címkével fogjuk megjelölni. A futási idej verem használata A verem egy hardveresen támogatott ábrázolású adatszerkezet. Adatot betenni a push1, kivenni a pop1 utasításokkal lehet. A paraméter 16 vagy 32 bites lehet, 8 bites nem. A pop operandusa nem lehet konstans. Hibás Példa push push eax dword [esi] push pop word 5 ecx push al pop dh
A veremmveletek 386-ostól felfelé a következ mveletekkel ekvivalensek hatásukban.
push adat mov [esp], adat értéke
sub esp, adat hossza ; 2, ha szó, 4, ha duplaszó
pop adat add esp, a mozgatott adat hossza
mov cél, [esp]
Fontos, hogy rögzítsük, hogyan kerülnek át a paraméterek a hívás helyérl az alprogramba, illetve hogyan adja vissza az alprogram a visszatérési értékét, ha van neki; ezt hívási konvenciónak nevezzük. A cdecl konvenció a paramétereket jobbról balra haladva teszi a verembe, a visszatérési értéket pedig az eax regiszterbe teszi. Ha a visszatérési érték kisebb (pl. ASCII karakter), csak ax vagy al tartalmaz értékes adatot, a fels bitek értéke figyelmen kívül hagyható; ha nagyobb struktúra, például egy rekord, eax-ban a struktúra címét adjuk vissza. a bevezet kód push ebp az alprogram mov ebp, esp elején sub esp, lokális paraméterek összhossza (byte) mov esp, ebp a kilép kód az pop ebp alprogram ret végén
Az alprogramot a fenti kódrészletekkel kezdjük és fejezzük be. Az alprogramon belül az i-edik paramétert a [ebp + 4 + i * 4] címen, a j-edik lokális változót az [ebp - j * 4] címen érhetjük el. Az alprogram egy futó példányához tartozó információk összességét aktivációs rekordnak nevezzük. Ez tartalmazza az összes paramétert és a lokális változót. A futási idej veremben, veremkeretekben tároljuk az aktivációs rekordokat. Ezek megvalósításáról szól a következ oldal.
Kitlei Róbert
Assembly programozás
25
Konkrét példa a futási idej verem használatára: a faktorialis( 2 ) hívás megvalósítása
Kezdetben a verem tartalma lényegtelen számunkra. Annyit tudunk csupán, hogy a verem tetejének a címe esp-ben található. Amikor meghívjuk faktoriális függvényt, a paraméterét a verembe tesszük. Ezzel esp is csökken néggyel, és a verem új tetejére mutat. Ezzel az utasítással hívjuk meg a függvény kódját. Hatására a verembe bekerül a következ utasítás címe; visszatéréskor innen tudjuk majd, honnan kell folytatni a végrehajtást. Ezután a vezérlés átkerül a függvény kezdcímére (ezt tartalmazza a faktorialis címke).
esp
>>>>>>
esp
a verem alja >>>>>>>>>
esp
push dword 2
par1: 2 régi eip par1: 2
esp
... ...
call fakt
add esp, 4
Miután végrehajtottuk a függvény kódját, a verembl visszaáll eip regiszter értéke, és a végrehajtás ennél az utasításnál folytatódik. Itt még vissza kell állítanunk a vermet az eredeti állapotába, vagyis esp-hez annyit adnunk hozzá, ahány byte-ot a paraméterekkel elfoglaltunk a verembl. Készen vagyunk, innentl folytatódhat a program futása. Az eax regiszter tartalmazza 2! értékét.
par1: 2
esp
... ...
Konkrét példa a futási idej verem használatára: a faktorialis függvény kódja
fakt
push ebp mov ebp, esp sub esp, 4
push ebx cmp dword [ebp+8], 0 jne .hamis.ag mov dword [ebp-4], 1 jmp .elagazas.vege .hamis.ag mov eax, [ebp+8] dec eax push eax call fakt add esp, 4 mov ebx, [ebp+8] mul ebx mov [ebp-4], eax .elagazas.vege mov eax, [ebp-4] pop ebx mov esp, ebp pop ebp ret
>>>>>>
A függvény kezdetét címke jelöli, hogy könnyen meghívható legyen. A szokásos bevezet kód. Az els két sor beállítja ebp-t arra a esp pozícióra, ahonnan a függvény végéig nem mozdul el. v Ezért ebp-t a veremkeret bázisának nevezzük. ebp-4 A harmadik sor lefoglalja a helyet a lokális változónk, v számára. Elmentjük a függvényben használt ebx-et. esp A cdecl alprogramhívási konvenció szerint az alprogramok régi ebx v eax, ecx és edx tartalmát változtathatják meg. ebp-8 ebp-4 A második sorban szerepl elágazás feltétele és igaz ága. Az elágazás igaz ága. A v változó értéket kap, majd elugrunk az elágazás végére, hogy ne fussunk bele a hamis ágba.
esp
régi eip par1: 2
ebp+4
...
ebp+8 ebp+12
régi ebp régi eip par1: 2
ebp ebp+4
...
ebp+8 ebp+12
régi ebp régi eip par1: 2
ebp ebp+4
...
ebp+8 ebp+12
régi ebx v: 1
ebp-8 ebp-4
régi ebp régi eip par1: 2
ebp ebp+4
...
ebp+8 ebp+12
Kiszámítjuk és a verembe helyezzük az egy szinttel mélyebben lev függvény paraméterét. Hasonló az alprogram küls példányának fent leírt meghívásához. A függvény meghívása, majd a hívás paraméterének eltávolítása a verembl. Az elbb eax-be kiszámított fakt( n 1 ) és az ebx-be betöltött n szorzatának kiszámítása v-be. Feltesszük, hogy a szorzat belefér eax-be. Akármelyik ágon is jutottunk el ide, a veremkeret szerkezete ugyanúgy néz ki. Eltér v, azaz [ebp-4] tartalma. A visszatérési érték eax-be kerül. A használt regiszter visszaállítása. A hamis ágban a szorzás felülírta edx-et is, azonban ezt nem kell visszaállítani az alprogram kezdeti értékére.
esp esp
régi ebx v: 2
ebp-8 esp ebp-4
régi ebp régi eip par1: 2
ebp ebp+4
...
ebp+8 ebp+12
régi ebx v: ?
ebp-8 ebp-4
régi ebp régi eip par1: 2
ebp ebp+4
...
ebp+8 ebp+12
v: ?
ebp-4
régi ebp régi eip par1: 2
ebp ebp+4 esp
...
ebp+8 ebp+12
A feleslegessé vált lokális változókat átugorjuk, majd visszaállítjuk ebp-t is. Ekkor ebp az elz szinten lev veremkeret bázisát mutatja.
régi eip par1: 2
...
Visszatérünk. A program futása a call faktorialis hívás után következ kódsornál folytatódik.
A veremkeret általános szerkezete
<<<<<<<<< verem ...
ebp-...
lokVált2 lokVált1 régi ebp régi eip
ebp-8 ebp-4 ebp ebp+4
par1
ebp+8
par2
...
az elz aktivációs rekordok >>>>>>>>>
ebp+12 ebp+...
26
Assembly programozás
Kitlei Róbert
Parancssori paraméterátadás A parancssori paraméterek átadása rendszerenként különbözik, azon belül szerkesztprogramok között is eltér lehet. Linux alatt, gcc szerkesztvel nagyon kényelmesen érhetek el a paraméterek, mivel a fprogram is úgy viselkedik, mint egy int main( int argc, char** argv ) szignatúrájú C függvény. Ennek megfelelen az elzekben leírt alprogramparaméter-elérési módszerek alkalmazhatóak. section .text global main main push ebp mov ebp, esp
ebp ebp+8 ebp+12
<<<<<<< futási idej régi kitölt argc <<<<<<< verem ebp dword ; alprogram bevezet kódja
argv
4 2
0
[ebp+12]
[ebp+12]+4
[ebp+12]+8
mov eax, [esp+4] ; parancssori paraméterek száma plusz egy, filenév 1. paraméter 2. paraméter ; ami argv[0], a futtatott program neve cmp eax, 2 jne .nem_egy a . o u t
0
...
; ha nem pontosan egy parancssori paramétert kaptunk, kilépünk
a b c
0
mov esi, [ebp+12] ; esi = argv mov esi, [esi+4] ; esi = argv[1], az els paraméter .szamlal mov al, [esi] cmp al, 0 je .tovabb inc esi jmp .szamlal .tovabb mov edx, esi sub edx, ecx mov mov mov mov int eax, ebx, ecx, ecx, 0x80 4 1 [esp+8] [ecx+4] ; byte-onként léptetjük a mutatót, mert a szöveg ASCII karakterekbl áll ; megszámoljuk, milyen hosszú (ASCII string)
; hossz: a nulla tartalmú végpozíció a szöveg kezdete ; kiírás következik, bvebben lásd késbb
.nem_egy ... A memóriában ezek egy potenciális elhelyezkedése két paraméter ('x5' és egy csillag) esetén a következ. A verem 5AC17C40 és 72543214 hexa címek között található, azaz 98872693 duplaszó fér bele. Az argc paraméter címe 72543218 (értéke 3), argv címe 7254321B. A filenév címe, argv értéke AA5C21BE, az els paraméterre mutató argv[1] címe AA5C21C2, a második paraméterre mutató argv[2] címe AA5C21C6. A filenév szövegesen a 206D0C1F és 206D0C26 címek között helyezkedik el (a végén egy hexa nullával), az els paraméter A47C3201 és A47C3203 között, a második A47C31FF és A47C3200 között.
206D0C1F 5AC17C40 0 72543214 ? ? ? ? 03 00 00 00 BE 21 5CAA A47C31FF AA5C21BE 0 1F 0C 6D 20 01 32 7C A4 FF 31 7C A4
p r g . e x e
verem
*
0
x 5
Kitlei Róbert
Assembly programozás
27
A rendszerszolgáltatások elérése: fájlkezelés
mvelet létrehozás megnyitás fájlmveletek olvasás írás pozicionálás lezárás kilépés név11 creat open read write lseek close exit eax 8 5 3 4 19 6 1 hibakód a fájl leírója ebx a fájlnév címe 0 olvas 1 ecx elérési jogok ír 2 ír/olvas puffer címe puffer címe edx elérési jogok max. hossz (byte) hossz (byte) vissza eax fájlleíró fájlleíró olvasott byte-ok írt byte-ok száma pozíció a fájlban 0 OK 1 hiba
eljeles eltolás az edx-ben kapott 0 elölrl 2 hátulról pozícióhoz képest 1 az aktuális pozícióról -
Az operációs rendszer szolgáltatásait megszakítások meghívásával lehet elérni. Ezek két dologban térnek el az alprogramhívástól: egyrészt a paramétereket más konvenció szerint, a regisztereken keresztül adjuk át, másrészt nem a call, hanem az int utasítást használjuk, mert más védettségi szint kódot az operációs rendszerét érjük el. Az int-nek paraméterül mindig 80h-t fogunk adni, ami a Linux rutingyjteményének sorszáma a megszakítások között. Más operációs rendszerek is nyújtanak hasonló szolgáltatásokat, általában más sorszámú megszakítás alatt, és teljesen eltér paraméterezéssel. A szolgáltatások megváltoztatják az eax, ecx és edx regisztereket. Hiba esetén -1-et adnak vissza. Egy fájl életciklusa a következképpen alakul. · létrehozás: ez a megnyitás egy speciális fajtája, a creat egy részben elre felparaméterezett open · megnyitás · a megnyitandó fájlnév, amelynek a címét paraméterként kapja, egy nullára végzd ASCII string · az elérési jogok: egy-egy bit jelzi, hogy a felhasználó/csoport/külvilág írhatja/olvashatja/futtathatja-e a fájlt12. Ezt a legegyszerbb nyolcas számrendszerben leírni13. · siker esetén egy 32 bites leírót kapunk vissza, innentl ennek a segítségével hivatkozhatunk a fájlra. Elre megnyitott fájlok a sztenderd bemenet, kimenet és hibafolyam. Ezek leírói sorban 0, 1, és 2. · írás, olvasás · meg kell adnunk egy mutatót a forrás-, illetve célterületre (ez a puffer), és azt, hány byte-ot szeretnénk átvinni. · a fájlhoz tartozik egy mutató, amely kezdetben a fájl elején áll. Ezt minden olvasási és írási mvelet mozgatja, de közvetlenül is lehet állítani. Figyelni kell arra, hogy a fájl végérl negatív értékkel tudunk elrébb pozicionálni. A fájl végén, ha túlmutatunk rajta, az átlépett pozíciókon 0 byte-ok szerepelnek. · lezárás: a program befejezdésekor a rendszer lezárja a nyitva maradt leírókat, de kézzel is megtehetjük. mov mov mov mov int eax, ebx, ecx, edx, 80h 4 ; kiírást fogunk igénybe venni 1 ; a kiírás a sztenderd kimenetre történik uzenet ; az üzenet címe 12 ; a kiírandó üzenet hossza ; kilépés következik ; nullás hibakód: minden rendben történt
section .data uzenet db db db db "Hello" ' ' "vilag" 0xA
Példa 28
mov eax, 1 xor ebx, ebx int 80h
11 további információk találhatóak az /usr/include/asm/unistd.h fájlban, illetve a man 2 név parancs használatával 12 például a ,,bárkinek bármit" 777q, a ,,felhasználónak bármit" 700q, a ,,felhasználó írhatja, bárki olvashatja" 644q 13 egy nyolcas számrendszerbeli szám 0 és 7 közötti számjegyeket tartalmaz, és 'q' vagy 'o' áll a végén
Assembly programozás
Kitlei Róbert
Feladatok 1. a. Írd ki a ,,Hello vilag!" szöveget betrl betre bvülve! Elször csak egy ,,H" bett írj ki és egy sorvégét, aztán a ,,He" szöveget és sorvégét stb. b. Hasonlóan bvül módon írd ki az els parancssori paramétert! 2. a. i) Alkoss a saját (lefordított, futtatható) programkódját kiíró programot14! Feltételezheted, hogy a program neve ismert. Használj 1024 bites puffert a fájl tartalmának beolvasása során! ii) Nehezítés: a program nevét a parancssori paraméterek közül kell kinyerned. b. i) Alkoss a saját forrásszövegét kiíró programot! Feltételezheted, hogy a program neve ismert. ii) Nehezítés: a program neve a futtatott fájl neve .asm kiterjesztéssel. Feltételezheted, hogy a fájl neve kevesebb, mint 256 karakter hosszú. 3. Írj olyan eljárást, amely egy duplaszón elfér, eljel nélküli egész számot ír ki a képernyre tízes számrendszerben! A számot paraméterként kapja meg az eljárás. A kiírandó string legyen lokális változó (fix hosszúságú ASCII karaktertömb); az eljárásnak ezt töltse fel, majd hívja meg a kiírást. 4. Írj programot, amely a következképpen mködik. A program két paramétert kap: egy filenevet és egy három karakteren ábrázolt, eljel nélküli számot, n-et, amely értéke ábrázolható egy byte-on. Olvasd fel a file els byteját, m-et. Vizsgáld meg, a file n-edik és m-edik byte-ja megegyezik-e. A program meghívása: Példa ./a.out 004 file.txt ekkor n = 4
A file.txt tartalma (hexa számok): 02 07 FD 04 ... ekkor m = 2, a második byte 7, a negyedik 4, nincs egyezés, 01 AB 7C 01 ... ekkor m = 1, az els és a negyedik byte egyaránt 1, egyeznek
5. Írj programot, amely a következképpen mködik. A program egy filenevet kap paraméterként. A file hossza hárommal osztható. A program vizsgálja meg, hogy minden harmadik byte egyenl-e az elz kett összegével (modulo 256, azaz 0xAB 0xCD 0x78 helyes). 6. Írj programot, amely a következképpen mködik. A program egy filenevet kap paraméterként. A program keresse meg, melyik az az els pozíció, ahol a file elölrl és hátulról olvasva eltér. Feltételezhet, hogy van ilyen pozíció. Példa Példa: ha a file tartalma abcdecba, akkor a program eredménye 4.
7. Add meg, hogy a parancssori paraméterként kapott file a legvégén tartalmazza-e a saját nevét. 8. Add meg a következ függvények kódját: f( 0 ) = 2 f( 1 ) = 1 f( n ) = f( n 1 ) 2 * ( f( n 2 ) or f( n 3 ) ) g( 0 ) = 2 g( 1 ) = 1 g( n ) = ha f( n 1 ) f( n 2 ) : ( f( n 1 ) - n ) xor f( n 2 ) különben : f( n 1 ) xor ( f( n 2 ) + n )
14 A feladat jellegébl következen a kilistázott tartalom meglehetsen olvashatatlan lesz, érdemes a diff programot használni az eredmény vizsgálatára.
Kitlei Róbert
Assembly programozás
29
9. Adott egy inteket tartalmazó tárhely kezdcíme és a hossza. a. Adott egy int kovetkezo() szignatúrájú függvény. Töltsd fel a tárhelyet sorban az 1, 2, 3 stb. számokkal úgy, hogy a függvény mondja meg, hány pozíciót kell az adott értékkel feltölteni! Példa Példa Ha a visszatérési értékek sorban 1, 3, 2, 1, akkor a feltöltött tárhelynek így kell kinéznie: 1, 2, 2, 2, 3, 3, 4. b. Adott két index, melyekre teljesül, hogy 0 < i j hossz, valamint egy int f( int ) szignatúrájú függvény. Töltsd fel a tömböt úgy, hogy tetszleges k indexre f(k) + tomb[k-1] kerüljön a tömbbe, ha i k j, különben pedig k. c. Adott egy int permutal() szignatúrájú alprogram. Ennek segítségével permutáljuk a tömböt helyben a következképpen. A permutal() els meghívásakor azzal a pozícióval tér vissza, ahová az eredeti els elemnek kell kerülnie. A következ híváskor a visszatérési érték az elbb lecserélt elem új pozíciója lesz. Ez addig folytatódik, amíg az els pozícióhoz vissza nem jutunk. Ha az eredeti tömb tartalma 6, 2, 9, -4, 3, a visszatérési értékek sorban 3, 5, 4, 1, 2, akkor a tömb új tartalma -4, 3, 6, 9, 2 lesz.
10. Valósítsd meg a következ függvényt! int osszegzo( int* tomb, int hossz ) { int i, j; int osszeg = 0; for ( i = 0; i < hossz; ++i ) for ( j = i; j < hossz; ++j ) { if ( tomb[i] <= tomb[j] ) osszeg = osszeg + tomb[i]; else osszeg = osszeg + tomb[j]; } return osszeg; }
30
Assembly programozás
Kitlei Róbert
Makrók Az eddigiekben az assembly nyelvek fordításának utolsó fázisát tárgyaltuk, amikor az assembler a forrásprogramból gépi kódot állít el, amely futási idben mködik: megváltoztatja a regiszterek értékét, alprogramokat hív meg stb. Ebben a fejezetben a fordítási idben, az elfordítási fázisban mköd makrókkal foglalkozunk. Ezek a forráskód általunk megírt szövegét alakítják át; csak azután kezd el mködni a gépi kódra fordító fázis, hogy a makrófordító fázis véget ért. Tartsuk végig szem eltt, hogy bármennyire is hasonló dolgokat lehet elvégezni a makrók segítségével, mint az utasításokkal, lényeges különbség, hogy a makrók nem férnek hozzá a regiszterekhez és a memóriához, és nem tudnak elugrani a kód más pontjaira. A nasm assembler -e kapcsolójával lehet az elfordítási fázis végeredményét kiíratni. A szimbólumok a fordítóprogram által értelmezett, a forráskódban elforduló karaktersorozatok. Már találkoztunk a szimbólumok egy fajtájával: a címkék egy memóriacím szimbolikus megjelenési formái. A mnemonikok és a regiszterek nevei nem szimbólumok, hanem kulcsszavak; ezeket a neveket is fel lehet venni a szimbólumok közé is, de határozottan nem ajánlott. A makrók a szimbólumok paraméterezhet, felüldefiniálható fajtája. Az elfordítási fázis a következképpen mködik. Az assembler lineárisan végighalad a kódon, és sorban azt vizsgálja, hogy talál-e makródefiníciókat vagy makróhívásokat. A makródefiníció tartalmazza a makró nevét, paramétereit és törzsét, amely függ a paraméterektl. Innentl, ha az assembler olyan szimbólumsorozattal találkozik, amely a makró nevével kezddik, és megfelel számú szimbólummal folytatódik (úgy, hogy azok értelmezhetek legyenek a makró paramétereiként), azt makróhívásnak tekinti, és kifejti. Ez úgy történik, hogy a kifejtend kódrészletet, vagyis a makró nevét és a paraméterezést, eltávolítja a forrásszövegbl, majd a helyébe beszúrja a makró törzsének az aktuális paraméterek szerint meghatározott alakját. A fordítóprogram ezután folytatódik a kifejtett rész kezdetétl, ezért a kifejtés után rögtön annak a vizsgálata következik, hogy a beillesztett részben található-e újabb kifejtend makróhívás. Figyelem: az alprogram paraméterei, a makró paraméterei és a parancssorból átvett paraméterek három teljesen különböz fogalom! Adódhat feladat, mely során olyan makrót kell írni, amely paraméterül kapja, hogy egy alprogram melyik paraméterei jelentenek fájl-paramétert, és ennek megfelelen kezeli ket... Egysoros makrók A makróknak egy egyszer fajtája az egysoros makró. Egyszersége abból fakad, hogy egyetlen sorra tud csak kifejtdni, ami legfeljebb egy utasítást tartalmazhat, ezért bonyolultabb kódot nem lehet vele generálni. Az egysoros makrókat a %define makrónév tartalom vagy a %xdefine makrónév tartalom kulcsszó vezeti be, utána kell leírni a makró nevét, majd zárójelek között, ha vannak, a formális paramétereket, majd a makró törzsét. A formális paraméter szintén szimbólum, egy névvel jelöli meg a paraméter pozícióját; kifejtéskor a törzsben minden elfordulása lecseréldik a makróhívás megfelel pozícióján szerepl aktuális paraméterre. A makródefiníciók felüldefiniálhatóak: az azonos nev és paraméterszámú makródefiníciók közül az utoljára elemzett van érvényben. A tartalomban érdemes megfelelen zárójelezni a paramétereket. %define %define helytelen(a, b) helyes(a, b) a*b ((a)*(b))
Példa
mov eax, helytelen(2, 3 + 4) ; erre fejtdik ki: mov eax, 2 * 3 + 4 mov eax, helyes(2, 3 + 4) ; erre fejtdik ki: mov eax, ((2) * (3 + 4)) Az els sor is érvényes utasítás, de a programozónak valószínleg nem ez volt a szándéka.
Kitlei Róbert
Assembly programozás
31
A %xdefine annyiban tér el a %define-tól, hogy míg az utóbbit alkalmazva a makró értéke a szövegszeren lemásolt törzs lesz, addig az elbbi a törzs kifejtett alakját veszi fel a szimbólum tartalmának, ezért az szövegszeren nem tartalmaz már makrókat. Ennek akkor van szerepe, ha a definíció és a kifejtés helye között felüldefiniálunk egy olyan makrót, amely szerepel a törzs szövegében: a %define esetében ez hatással lehet az eredeti makróra, a %xdefine esetében nem. %define %define %xdefine %define a b c a 1 a a 2 ; erre fejtdik ki: mov eax, 2 ; erre fejtdik ki: mov eax, 1
Példa
mov eax, b mov eax, c
A nullától eltér paraméterszámú makrók túlterhelhetek is: érvényben lehet egyszerre több olyan makródefiníció is, amelyeknek ugyanaz a neve, de eltér a paraméterszáma. A makródefiníciók nem rekurzívak: ha a kifejtésen belül újra találkozik az assembler ugyanazzal a makróval, azt nem fejti ki még egyszer. Az egysoros makrók további, speciális fajtája a fordítási idben történ értékadás. A %assign szimbólumnév érték kulcsszó után egy szimbólumnév következik, majd a sor további része egy fordítási idben kiértékelhet kifejezés15, amely meghatározza a szimbólum felvett, új értékét. Ez hatásában nagyon közel áll ahhoz, mintha %define segítségével hoztunk volna létre egy szimbólumot, a különbség egyrészt abban áll, hogy a %define nem csak számokkal dolgozhat, másrészt pedig a %assign a definíció során kiértékeli a kifejezést, ezért tömörebben tudja az assembler ábrázolni. További elnye, hogy ránézésre egybl látszik róla, hogy szám jelleg szimbólummal dolgozunk. %assign %assign ... %assign n 1 n n+1 ; összesen hússzor n n+1 %define %xdefine ... %xdefine n 1 n n+1 ; összesen hússzor n n+1
Példa
mov eax, n ; erre fejtdik ki: mov eax, 21
mov eax, n ; erre fejtdik ki: mov eax, 1+1+...+1
Ezzel rokon konstrukció a konstans szimbólum. Alakja: szimbólumnév equ kifejezés. Fontos, hogy a kifejezésnek fordítási idben kiértékelhetnek kell lennie. A szimbólum felveszi a kifejezés értékét, mintha %assign segítségével definiáltuk volna, azonban innentl nem változtatható meg az értéke. negyvenketto equ 4 * 10 + 2 ; ekvivalens ezzel: mov eax, 42
Példa
mov eax, negyvenketto
15 konstansok, makrókkal definiált szimbólumok és azokkal végzett mveletek; ha ide egy regiszter, pl. eax nevét írjuk, az nem hiba, de a fordítóprogram érdekldni fog, hogy mi az eax szimbólum tartalma, ha nem definiáltuk
32
Assembly programozás
Kitlei Róbert
Többsoros makrók Bonyolultabb mködés leírására alkalmasak a többsoros makrók. Ezek törzsét a %macro és a %endmacro (rövidíthet: %endm) kulcsszavak közé lehet leírni. A kulcsszavakat külön sorba kell írni; a %macro kulcsszó után a sorban következik a makró neve, a paraméterek száma, és ha vannak, az alapértelmezett paraméterek. Makródefiníciókat törölni akár egysorosakat, akár többsorosakat %undef makrónév leírásával lehet. A paraméterek számának alakjai a következk lehetnek. · egyetlen szám: pontosan ennyi paraméterrel kell meghívni a makrót · két szám kötjellel elválasztva: a paraméterszámnak bele kell esnie ebbe a (zárt) intervallumba · a paraméterek száma valójában a második szám; meg kell adni vesszkkel elválasztva, milyen értékeket vegyenek fel azok a paraméterek, amelyeket a makróhívás nem adott meg explicit módon · egy szám, kötjel és csillag: a paraméterszám legalább annyi, mint az els szám, fels korlát nincs · a makró törzsének írásakor nem ismert, hány paramétert kapott · az aktuális paraméterek számát %0 tartalmazza. A makrókat lehetséges specializálni is, azaz egyes paraméter-kombinációkhoz külön kódot is meg lehet adni. A formális paraméterekre, mivel most nincsenek névvel ellátva, %1, %2 stb. leírásával lehet hivatkozni a törzsben. Fleg többsoros makrók használatánál fordul el, hogy vesszt is tartalmazó paramétereket is át szeretnénk adni. Ezeket kapcsos zárójelek közé kell tenni híváskor, mert különben az assembler külön makróparamétereknek nézné ket. Amikor a törzsben felhasználjuk ket, már nincs körülöttük a kapcsos zárójel, ezért ha egy másik makrónak szeretnénk átadni ket, akkor ismét be kell kapcsos zárójelezni ket. %macro egyop 2 %1 %2 %endm egyop inc, eax ; erre fejtdik ki: inc eax %macro ketop 3 %1 %2, %3 %endm ketop mov, eax, ebx %macro hanyop 1-* mov eax, %0 - 1 %endm hanyop 1, 2, 3, 4, 5
Példa
; erre fejtdik ki: mov eax, ebx ; erre fejtdik ki: mov eax, 5 - 1
Feltételes ugró utasítások feltétel-részét is átadhatjuk makróparaméterként, a makró törzsében pedig a j bet után fzve megkapjuk a megfelel feltételes ugró utasítást. A j bet után %-1 fzésével az ellentétes hatású ugró utasítást kapjuk; természetesen 1 helyett tetszleges sorszám szerepelhet. %macro felteteles 5 cmp %1, %2 j%+3 %4 j%-3 %5 %endm felteteles eax, ebx, ne, .nem_egyenlo, .egyenlo ; erre fejtdik ki: ; ; cmp eax, ebx jne .nem_egyenlo je .egyenlo
Példa
Kitlei Róbert
Assembly programozás
33
A makróra lokális szimbólumokat %%szimbólumnév alakban készíthetünk. Ezek a makró kifejtése során szimbólumnév egyedi nevekre fordulnak le, vagyis a makró két kifejtésével különböz neveket kapunk ugyanabból a lokális szimbólumból. Ennek akkor van szerepe, ha címkékre vagy számítások végzéséhez szimbólumokra van szükségünk, azonban azt szeretnénk, hogy a makró kifejtése eltt definiált szimbólumokat a kifejtés után is fel lehessen használni: a makró ne változtassa meg ket. Ezért nem egy véletlenszeren választott szimbólumnevet használunk a makrón belül, amelyrl nem tudjuk, létezik-e már, és kockáztatjuk, hogy felülírjuk, hanem egy olyan lokális nevet vezetünk be, amelyrl az assembler garantálja, hogy a makrón belül jelenik meg elször. Százalékos prefixszel csak konkrét makróparaméter-pozíciókra lehet hivatkozni. Nem konstansként megjelen (pl. paraméterként kapott) index makróparamétert a makróparaméterek forgatásával lehet elérni. Tipikusan ilyen eset áll el, amikor egy nemkorlátos paraméterszámú makró paramétereit szeretnénk elérni. Ennek eszköze a %rotate kulcsszó: utána egy fordítási idben kiértékelhet kifejezést írva megváltoztathatjuk a makróparaméterek sorrendjét. Például %rotate 3 hatására %1 az eddigi %4 értékét veszi fel, %2 %5-ét stb., illetve a régi %1, %2 és %3 innentl %9, %10 és %11-ként érhetek el, ha összesen tizenegy paraméterünk volt. A %rotate után negatív szám is állhat, ekkor a másik irányba forognak a makróparaméterek. Példa mov eax, %1 + %2 db %3, %2, %4 push %5 xor eax, %xyz sub ecx, %%%a add edx, %{%%b} inc %%0 mov eax, %{%0} Hibás
Ha a forráskód egy részét szeretnénk konstans sokszor megismételni, a %rep és %endrep kulcsszavak között írhatjuk le. A %rep után fordítási idben kiértékelhet kifejezésként kell megadni, hányszor akarjuk kifejteni az ismétlend kódrészletet. Ez a konstrukció makrókon kívül is használható, azonban a leggyakrabban nemkonstans paraméterszámú makrók paramétereinek bejárására alkalmazzák úgy, hogy a mag utolsó sora egy %rotate-tel forgatja a makróparamétereket. Írjunk olyan makrót, amely a kapott paramétereinek összegét számolja ki eax regiszterbe! %macro osszeg 0-* %define %%masikmakro %1 %define %%kezdet %2 %define %%darab %3 %rotate %%kezdet + 2 %rep %%darab %%masikmakro %1 %rotate 1 %endrep %endm forgat mar_definialt_makro, 2, 3, "a", "b", "c", "d", "e", "f", "g" ; ennek hatására a következ makróhívások keletkeznek a kódban: ; mar_definialt_makro "b" ; mar_definialt_makro "c" ; mar_definialt_makro "d" ; azok pedig továbbfejtdnek annak a makrónak a kódja szerint
34
Példa
Assembly programozás
Kitlei Róbert
Feltételes fordítás makrókkal Gyakran elfordul, hogy egy kódrészletet csak akkor akarunk belefordítani a kódba, ha valamilyen fordítási idben kiértékelhet kifejezés teljesül. Természetesen ezt kézzel is meg tudnánk tenni, azonban nagyon kényelmes lehet, hiszen így a forráskód egyetlen pontját megváltoztatva több helyen is megváltoztathatjuk a program mködését. Jellemz alkalmazás a hibakeresés, amikor egy hibakeres szimbólumot átállítva, annak tartalmától függen, a kódba különböz hibakezelési, naplózási stb. részletek is belekerülnek a megfelel pontokon. Az általános feltételes fordítást a %if feltétel konstrukcióval végezhetjük. Ennek hatására az ettl az %endif kulcsszóig terjed kódrészlet csak akkor jelenik meg a kódban, ha a feltétel fordítási idben igazra értékeldik ki. A feltételes fordításhoz további ágakat írhatunk %elif feltétel alakban, illetve lezáró ágat %else alakban. %macro eliranyit 3-5 %if %0 = 3 %1 %2, %3 %elif %0 = 4 %1 %2, %3, %4 %else %1 %2, %3, %4, %5 %endif %endm eliranyit hivott_makro, 1, 2 eliranyit hivott_makro, 1, 2, 3 eliranyit hivott_makro, 1, 2, 3, 4 ; ennek hatására az els paraméterben megadott makrónak mindig a megfelel paraméterszámú változata ; hívódik meg Hasonló konstrukciókkal ellenrizhetjük makrók létét. Egysoros makrók létének vizsgálatára alkalmas az %ifdef makrónév, amelyet elszeretettel alkalmaznak a fent említett hibakeresésre; ennek a további ágait %elifdef makrónév alakban adhatjuk meg. Többsorosakat az %ifmacro makrónév paraméterszám segítségével vizsgálhatunk, ahol a paraméterszám lehet egy konkrét szám, illetve egy intervallum. A fentiekben hasznos lehet a %error üzenet direktíva, amely fordítási idben kiírja az utána megadott üzenetet. %macro ellenor 2 %ifdef %1 %1 %2 %else %error Nem talalhato a makro: %1 %endif %endm eliranyit nem_letezo_makro, 12456 ; ennek hatására a fordítóprogram ezt írja ki: a.asm: warning (ellenor:4) Nem talalhato a makro: nem_letezo_makro Még egy hasznos direktíva: fájlt beilleszteni a forráskód adott pontjára így lehet: %include ''fájlnév''.
Példa
Példa
Kitlei Róbert
Assembly programozás
35
Írjunk olyan makrót, amely tetszlegesen sok paramétert kaphat. A paraméterek alprogramok címei, amelyek egy duplaszavas paramétert várnak. A makró generáljon olyan fprogramot, amely sorban meghívja az alprogramokat. A fprogram paraméterei 2 karakteren ábrázolt decimális számok; ha van elég parancssori paraméter, akkor az n-ediknek meghívott alprogram kapja paraméterként az n-edik parancssori paraméter értékét, ha nincs, akkor nullát. %macro alprogramhivo main %assign %%i %rep %0 mov mov mov mov mul add cmp setbe dec and movzx push call add 0-* 0
edx, [ebp+12] edx, [edx+4+4*%%i] al, [edx] cl, 10 cl al, [edx+1] dword [esp+4], %%i + 1 dl dl al, dl eax, al eax %1 esp, 4 1 %%i %%i + 1
Példa 36
%rotate %assign %endrep %endm
Assembly programozás
Kitlei Róbert
Feladatok 1. Írd meg az adc utasítást általánosan, kétparaméteres makróként. Az utasítás leírása egy korábbi feladatban szerepel. 2. Mit kapunk, ha kiíratjuk az adat címke tartalmát?
%macro rajzol 4 db %1 %rep %2 db ' ' %endrep db %3 %rep hossz - ( 2 * %2 ) db %4 %endrep db %3 %rep %2 db ' ' %endrep db %1 db 0ah %endmacro %define hossz adat.a rajzol rajzol rajzol rajzol rajzol rajzol rajzol db 0 ' ', ' ', '|', '|', '|', '|', ' ', 1, 0, 2, 0, 2, 0, 0, 7 'v', '=', 'X', ' ', '~', ' ', 'V', 'v' '=' 'X' ' ' '-' ' ' '-' %macro rajzol 5 db %1 %rep %2 - 1 db ' ' %endrep db %3 %rep %4 - %2 - 1 db ' ' %endrep db %5 %rep hossz - %4 - 1 db ' ' %endrep db %1 db 0ah %endmacro %assign hossz 8 adat.b %assign i 0 %rep 3 rajzol ' ', 4 - i, '/', 5 + i, '\' %assign i i + 1 %endrep db db db '+' '-------' '+', 0ah '|', '|', '|', '|', 5, 5, 1, 4, 'o', 'o', ' ', '|', 6, 6, 2, 5, 'o' 'o' ' ' '|'
rajzol rajzol rajzol rajzol db db
'=========' 0
3. a. Készíts deriváló makrót. A makró tetszleges számú paramétert kaphat, amelyek egy polinom együtthatóit adják. A makró tegye a verembe sorban a polinom deriváltjának együtthatóit szavakként! A deriválás szabálya: (c·xn)' = c·n·xn-1 b. Készíts integráló makrót a deriváló makróhoz hasonlóan. A konstans tagot nem kell figyelembe venni. Feltehet, hogy minden kapott együttható egész szám. A derival 1, 5, -4, 6 makróhívás esetén a polinom 1·x3 + 5·x2 - 4·x1 + 6·x0, a derivált 3·x2 + 10·x1 - 4·x0, a vermelend duplaszavak tehát 3, 10, -4. Az integral 6, -2, 3 hívás esetén az integrál 2·x3 - 1·x2 + 3·x1 + c, a vermelend duplaszavak: 2, -1, 3. 37 Példa
Kitlei Róbert
Assembly programozás
4. Készíts torpedó programot. a. Az adatok generálása makróval: egy olyan byte-tömböt kell létrehoznia, amely a paraméterként kapott pozíciókon 1-et (van hajó), különben 0-t (nincs hajó) tartalmaz. A makró tetszleges számú paraméterrel hívható, a pozíciók sorrendben egymás után jönnek. A hajo 2, 4, 7, 8, 9, 11 makróhívás szerint a következ tömböt kell kapnunk: db 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1 b. A program menete:
Példa Példa
mi lövünk beolvassuk a bemenetrl, melyik pozícióra lövünk meghívjuk az int mi_lovunk( int pozíció ) szignatúrájú C függvényt vele ha az egyet ad vissza, találatot értünk el ha kettt, egyúttal az ellenfél minden hajója elsüllyedt. Ekkor hívjuk meg a void nyert( void ) szignatúrájú C függvényt, majd lépjünk ki. különben nincs találat k lnek az int ok_lonek( void ) szignatúrájú C függvény adja meg, melyik pozícióra lttek ha volt ott hajónk, elsüllyed (nullázódik a pozíció) ha minden hajónk elsüllyedt, hívjuk meg a void vesztett( int általunk_kiltt_hajók_száma ) szignatúrájú C függvényt, és lépjünk ki.
5. Írj olyan makrót, amely 1 + legalább még 2 paramétert vár, és olyan kódot generál, amely eldönti, hogy a paraméterek (amelyek regiszterek és memóriatartalmak lehetnek) szigorúan növekv sorrendben vannak-e. Amennyiben nem, akkor ugorjunk az els paraméterben megadott címkére. Ha így hívjuk meg: vizsgal címke, eax, {dword [x]}, esi akkor azt kell megvizsgálnia a kódnak, hogy eax < dword [x] < esi teljesül-e.
6. Adott egy exp nev, háromparaméteres makró: exp x, 3, 4 beállítja az x szimbólumot 34 értékére. Készítsd el az ellenor n, m makrót, ami ellenrzi, hogy minden k-ra egytl m-ig helyesen számítja-e ki az exp makró nk-t. Azt kell megvizsgálni, hogy a makrót két szomszédos k-ra meghívva, a nagyobb érték megegyezik-e a kisebb érték n-szeresével. Ha jól mködik a makró, állítsuk be az ok szimbólumot egyre, ha pedig nem, akkor szüntessük meg az ok szimbólumot. 7. Adott egy Fibonacci nev, kétparaméteres makró: Fibonacci x, 4 beállítja az x szimbólumot a negyedik Fibonacci-szám értékére. Készíts olyan egyparaméteres makrót, ami ellenrzi, hogy a kapott paraméteréig jól mködik-e a Fibonacci makró. Azt kell megvizsgálni, hogy a kezdértékek jók-e, és hogy három szomszédos számra meghívva a makrót, a két nagyobbik kapott érték különbsége megegyezik-e a legkisebbel. Ha jól mködik a makró, állítsuk be az ok szimbólumot egyre, ha pedig nem, akkor szüntessük meg az ok szimbólumot. 8. Írj olyan makrót, amely tetszleges számú paramétert vár, és olyan kódot generál, ami megfordítja azokat a biteket eax-ben, amelyek sorszámát paraméterként kapta. A generált kód több utasításból is állhat, de minden egyéb regiszter értékét meg kell tartania. Ha eax tartalma kezdetben akkor fordit 1, 16, 3, 31 után 01110101011110101011110110101010, 11110101011110111011110110100000 lesz eax új tartalma. Példa 38
Assembly programozás
Kitlei Róbert
A program fordításának menete Amikor elkészült a program forráskódja, át kell alakítanunk futtatható állománnyá. Ennek els lépcsjében hívjuk meg az assemblert, amely még csak egy közbüls formátumot, tárgykódot állít el. A tárgykód már gépi kódot tartalmaz, de még nincsen készen: más tárgykódokban lev memóriacímekre való utalások lehetnek benne, és ezek csak a következ fázisban, az összeszerkesztés során válnak ismertté. A szerkesztprogram (linker) feladata az, hogy mindezen információk segítségével összeállítsa a végs, futtatható fájlt. A mi esetünkben az assembler neve nasm. A következ paramétereit használjuk a könyvben. meg kell adni az elkészítend tárgykód-formátumot a -f formátum kapcsolóval: számunkra ez mindig elf lesz meg kell adni a forrásfájlt, amelynek a kiterjesztését általában .asm-nak választjuk ha szükségünk van rá, készíthetünk listafájlt a -l listafájl-neve kapcsolóval megadhatjuk a kimeneti fájl nevét a -o fájlnév kapcsolóval, alapértelmezés szerint ez a fájl neve .asm helyett .o kiterjesztéssel Szerkesztprogramnak a gcc-t használjuk16. Paraméterei közül csak a tárgykód-fájl nevét kell megadnunk. Ez létrehozza a futtatható fájlt, amely az a.out nevet kapja, ha felül nem bíráljuk a -o fájlnév kapcsolóval. Ha mindent jól csináltunk, már csak futtatni kell az elkészült fájlt. Összefoglalva: a következ parancsokra lesz szükségünk egy assembly fájl fordításához. nasm -felf fájlnév.asm gcc fájlnév.o -o fájlnév ./fájlnév Amennyiben a program több tárgykódból áll, akkor a második lépés a következ alakú. gcc fájl1.o fájl2.o fájl3.o -o futtatható_állomány_neve Ha egy másik (akár más programnyelven megírt) fájlban akarjuk felhasználni valamelyik címkénket, akkor azt a global címkenév direktívával kell megjelölni.17 Mivel a címkenév nem tartalmaz információt a címkén tárolt adat vagy kódrészlet típusáról, ezeket a programnyelvben az importálás során leírt deklarációval kell elérhetvé tenni. Fordítva, ha egy másik tárgykódból használunk fel egy címkét, akkor azt az extern címkenév direktívával kell jelezni.
x.c gcc a.asm nasm a.o x.asm gcc x.o gcc a.out gépi kód y.adt gnat y.asm gas y.o z.hs ghc z.asm fasm z.o tárgykód assembly magas szint nyelv
16 A gcc többek között C nyelvrl assemblyre fordító fordítóprogramként is mködik. Fordítsunk le egy tetszleges, egyszer C programot a -S kapcsolóval! Ez egy más szintaxisú assemblyre fordít, mint a könyvben tárgyalt nasm. 17 Mivel a fprogramnak mindig láthatónak kell lennie kívülrl, a fprogram kódjában szerepelnie kell a global main sornak.
Kitlei Róbert
Assembly programozás
39
A gépi kód szerkezetérl Egy utasítás gépi kódja (ami egy bitsorozat) több mezre bontható. Általában vagy egy byte osztódik több mezre, vagy egy mez tartalmaz több byte-ot. Az utasítás következ bitjeinek értelmezése (milyen mezkbl áll) függhet az elz mezk értékétl. Az utasítások gépi kódjának legfontosabb mezje az operációs kód (röviden opkód). Ez dönti el, hogy milyen utasításról van szó. Közeli kapcsolatban áll a mnemonikkal, de mégis különböznek: a mov jelenthet 8, 16 vagy 32 bites adatmozgatást, az opkód azonban ezt az információt is tartalmazza. Az opkód maga is tartalmazhat mezket, például a duplaszavas adatot megnövel inc egy byte-os gépi kódjának alsó három bitje azt kódolja, hogy melyik regisztert növeljük meg. A byte operandusú inc gépi kódja ezzel szemben két byte hosszú. Az utasítások dekódolását tovább nehezíti, ha egyes mezk vegyesen értelmezettek. Egyes utasításoknál ugyanis elfordul, hogy egy mez lehetséges értékei közül egy adott bitminta speciális jelentést kap, a mez egyéb értékei pedig valamilyen más logika szerint szervezdnek. Például: az általános célú regisztereket el lehet kódolni három bit segítségével. Ilyen mezk szerepelnek majdnem minden utasítás gépi kódjában, mert a legtöbb utasításnak van regiszter operandusa, vagy tartalmazhat regiszteren keresztüli memóriahozzáférést. Azonban az [ebp] memóriacímzés nem kódolható el közvetlenül: legfeljebb egy regisztert használó memóriacímzés esetén az az érték, amely más esetekben ebp regisztert jelképezi, fenn van tartva a direkt (csak konstanssal való) memóriacímzés számára. Ezt a címzést más, ekvivalens módon kell elkódolni, [ebp+0] alakban. 0100 0100 0100 0100 0100 0100 0100 0100 0000 0001 0010 0011 0100 0101 0110 0111 inc inc inc inc inc inc inc inc eax ecx edx ebx esp ebp esi edi
Az utasítás most csak az opkódból áll. Az alsó három bit jelöli ki, hogy melyik regiszterrl van szó. 1111 1111 1111 1111 Példa 0000 0000 0000 0110 inc dword [eax] inc dword [esi]
A második byte alsó három bitje a fentihez hasonló szerepet tölt be. 1111 1111 0000 0101 0111 1000 0101 0110 0011 0100 0001 0010 inc dword [0x12345678]
A kivételes eset az, ami az elbb ebp regisztert kódolta. Memóriahozzáférés esetén az 101 bitsorozat a direkt címzés számára van fenntartva. Ekkor az utasítás kódjához tartozik még egy négy byte hosszú mez is, ahol maga a konstans jelenik meg. 1111 1111 1111 1111 0100 0101 0100 0110 0000 0000 1010 1011 inc dword [ebp] inc dword [esi+0xAB]
Az assembler mégis le tudja fordítani az utasítást, ha csak ebp regiszterrel címzünk. Úgy tekinti, mintha egy nulla konstanssal eltoltuk volna a címet. A második byte 6. bitje jelzi azt, hogy a címzésben konstans is szerepel. Maga a konstans egy egy byte-os mezn kap helyet. Ebben a konstrukcióban ebp ismét a többi regiszterhez hasonlóan van kódolva.
40
Assembly programozás
Kitlei Róbert
CISC-elv architektúrák A korai számítógépek számítási kapacitása alacsony volt, lassú a memória-hozzáférésük és kevés a memóriájuk, ezért tömör és hatékony kódot kellett rájuk készíteni. Ennek támogatására az utasításkészleteket úgy tervezték, hogy az egyes utasítások összetett számításokat legyenek képesek végrehajtani, amelyek gyakran megvalósíthatóak voltak más utasításokból alkotott rövid kódrészletekkel is. Innen ered a megközelítés neve: Complex Instruction Set Computer, röviden CISC. Egy példa erre az IBM 360-as nagygép, amelynek egyik utasításával egy bináris fa adatszerkezetet lehetett kiegyensúlyozni. A kódot az is tömörebbé tette, hogy bonyolult címzések segítségével egy utasítással lehetett adatelemeket lehetett egy lépésben elérni. Az x86 architektúrán, ha nem használhatnánk az összetett címzéseket, a következ kifejezést sokkal hosszabb kóddal tudnánk csak megvalósítani. Példa mov eax, [ecx+4*esi+28] mov mov shl add add mov eax, ebx, ebx, eax, eax, eax, ecx esi 2 ebx 28 [eax]
Természetesen a CISC architektúrájú gépek által végrehajtott gépi kód összetettsége is tükrözi a fentieket. Az egyszer utasítások gépi kódú alakjában sokkal kevesebb információnak kell megjelennie, mint az összetettekében. Az architektúrák tervezinek logikus döntése alapján ezért a gépi kódjuk is rövidebb. Így viszont a gépi kód végrehajtás eltti dekódolása bonyolultabbá válik, hiszen még az az információ sem áll rendelkezésünkre elzetesen, hogy hány byte alkotja a következ utasítást. Ebbl következen a processzorok bels szerkezete is bonyolultabb, mivel ott találhatóak az utasítások dekódolását végz áramkörök is. Az x86 architektúrán 1 és 15 byte közötti hosszúságú utasítások fordulhatnak el. RISC architektúrák Az 1980-as évekre nyilvánvalóvá vált, hogy a programok túlnyomó többsége fordítóprogram segítségével készül. Kiderült, hogy a fordítóprogramok kódgenerálás során általában nem használják ki a CISC architektúrák teljes kínálatát. A hardver sebessége és tárolókapacitása is rohamosan növekedett. A RISC (Reduced Instruction Set Computer) elv architektúrákat az új hardveres kritériumoknak megfelelen tervezték. Az utasítások többsége gyorsan tipikusan egy órajel alatt hajtható végre. Az utasítások egyszer szerkezetek, jellemzen minden utasítás gépi kódja azonos hosszúságú. Az utasítások szerkezete egyszer, kevés mezt tartalmaznak, az utánuk következ mezk szerkezetét lehetleg nem módosítják, ebbl következen könnyen dekódolhatóak. Az egyszer szerkezet csak egyszer címzéseket tesz lehetvé. Az x86 architektúra modern gépeinek processzora RISC elveken alapuló magra épül, amely értelmezi és végrehajtja azt a CISC kódot, amely a külvilág számára látható módon hajtódik végre a processzoron. Ezt a kétszint felépítést mikroarchitektúrának nevezzük.
Kitlei Róbert
Assembly programozás
41
Listafájl Az assembler által generált kódot a listafájlban tudjuk megtekinteni. Ezt a nasm -l listafájl kapcsolójával tudjuk létrehozni. A listafájl a tárgykód speciális nézete, amely a következket tartalmazza.
minden sor elején a sorszámát, a kódsor eltolását az utoljára megkezdett szegmens kezdetétl, a kódsorból generált byte-okat, a forrásprogramszöveget eredeti alakjában, soronként, a makrókifejtések szintjét.
A listafájlban megjelen számok hexadecimális számrendszerben vannak kódolva (a sorszámot kivéve). A listafájlból nem tudjuk közvetlenül kiolvasni az utasítások mezinek szerkezetét, mivel csak a generált byte-ok hexadecimális alakja jelenik meg benne.
sorszám cím a szegmens kezdetétl lefordított kód (byte-ok) az eredeti assembly kód változtatás nélkül
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
section .text global main 00000000 00000005 0000000A 0000000F 00000014 00000016 0000001B 00000020 B804000000 BB01000000 B9[00000000] BA04000000 CD80 B801000000 BB00000000 CD80 main mov mov mov mov int eax, ebx, ecx, edx, 0x80 4 1 szoveg 4
Példa
mov eax, 1 mov ebx, 0 int 0x80 section .data
00000000 00000006
48656C6C6F20 56696C61670A
szoveg
db db
"Hello " "Vilag", 0xA
A program betöltése A listafájlban és a tárgykódban nem a kód végleges alakja szerepel. Amikor a kódban egy memóriahivatkozás található, a fordítóprogram több okból sem tudja a kódot végs alakban generálni. Egyrészt elfordulhat, hogy a memóriahivatkozás egy másik tárgykódban szerepl címkére vonatkozik, amelyet az extern kulcsszóval jelöltünk meg. A címke pontos címét addig nem tudjuk eldönteni, amíg a másik tárgykódot nem generáltuk. Ezért olyan forrásfájlból, amely küls hivatkozásokat tartalmaz, nem lehet listafájlt készíteni. Másrészt még akkor is, ha minden címkénk ismert, a generált kód nem a végleges címeket tartalmazza. A program betöltésekor a kód- és adatszegmenseket a futtató környezet beolvassa és elhelyezi a memóriában. A szegmensek általában nem a 0 kezdcímre kerülnek, ezért az összes rájuk vonatkozó memóriahivatkozáshoz hozzá kell adni a szegmens kezdcímét. Ezért a kódszegmensben található mindegyik, az adatszegmensben lev adatokat elér memóriahivatkozásból generált kódot módosítani kell. Ezt relokációnak nevezzük.
42
Assembly programozás
Kitlei Róbert
Hibakeresés a programban A programok elkészítése során általában elsre nem sikerül tökéletes munkát végezni. Elfordulhatnak egyszerbb hibák, például lemarad egy záró zárójel; ezeket a fordítóprogram képes jelezni még azeltt, hogy futtatható állományt állítana el a kódból. Nagyobb fejtörést okoz azonban az, ha a program lefordul, elindul, viszont a végrehajtás során mást csinál, mint amit a programozó elvár tle. Ekkor meg kell keresni a hiba forrását, majd javítani kell. A hiba megkeresésére egy kézenfekv módszer, ha a program egyes pontjain jól látható módon szövegeket íratunk ki, amelyek jelzik számunkra, hogy a futás elérte az adott kódrészt. Ez a megközelítés azonban nehézkes, és kevés információt ad a hiba felmerülése idpontjában a regiszterek és a memória tartalmáról. Erre a célra külön hibakeres szoftvereket, angolul debugger programokat fejlesztettek ki. A továbbiakban a gdb hibakeres néhány funkcióját tekintjük át. Ha grafikus hibakeresre van szükségünk, a ddd programot használhatjuk. Tegyük fel, hogy a következ programban szeretnénk hibát keresni. A program eredetileg az els nyolc természetes számot írná ki egy-egy sorban, de elrontottuk. Fordítsuk le a programot a szokásos módon, majd indítsuk el a hibakerest a gdb paranccsal. Ekkor egy megkapjuk a hibakeres parancssorát. Minden parancs az egyértelmségig rövidíthet, ezt aláhúzással jelöljük a továbbiakban. Segítséget a help paranccsal tudunk kérni. section .text global main main mov ecx, 0 .ciklus cmp ecx, 8 je .vege push ecx call kiir add esp, 4 dec ecx jmp .ciklus .vege mov eax, 1 xor ebx, ebx int 0x80 kiir push ebp mov ebp, esp push ecx mov mov mov mov int eax, ebx, ecx, edx, 0x80 4 1 adat 2
pop ecx mov esp, ebp pop ebp ret section .data adat db db "0" 0xA
Kitlei Róbert
Assembly programozás
43
Elször is töltsük be a programot. Ahhoz, hogy a nasm-hoz hasonló formátumban írja ki a hibakeres az adatokat, adjuk ki a következ parancsot. Elször is nézzük meg a betöltött programot. Ehhez a következ két parancsot adjuk ki. Az els parancs a main címkétl a kiir címkéig írja ki az utasításokat, a második pedig a megadott címkét (jelen esetben a kiir-t) körülvev függvényt írja ki, ami most pont a kiir függvény lesz. Pontosan ugyanezt a kimenetet kapnánk például a disas kiir+3 parancs eredményéül. Ha a main.ciklus-t szeretnénk kiíratni, egyszeres idézjelek közé kell tennünk. Kényelmesen használható a tab gombbal az automatikus kiegészítés.
file kiiro set disassembly-flavor intel
disassemble main disassemble kiir disassemble kiir+3 disassemble 'main.ciklus'
Definiáljunk egy megszakítási pontot a program kezdetén. Lokális címkére így lehet break main megszakítási pontot definiálni: break *'main.vege' Ezután indítsuk el a programot, amely rögtön fenn is akad a megszakítási ponton. A programnak átadandó paramétereket a parancs után kell leírni. Hajtsuk végre lépésenként a programot. Ehhez a ni (következ utasítás, next instruction) parancsot alkalmazzuk. Csak egyszer szükséges begépelni, mivel üres parancssorban egy enter leütésével meg lehet ismételni az elz kiadott parancsot. Az ni-nek megadható, hány utasítást futtasson le egyszerre. A sok kiadott ni hatására látszik, hogy a programunk ciklizálni kezd: ugyanazok a címek tnnek fel sorban. Tegyük fel, hogy az a gyanúnk támadt, hogy a kiir függvényben van a gond; ekkor a stepi segítségével úgy hajtjuk végre a programot lépésenként, hogy a meghívott alprogramokba belépünk. Sajnos, ez sem segített, de újabb ötletünk támadt: mivel a kiir-t megnézve az jónak tnik, megvizsgáljuk, megfelel paraméterrel hívtuk-e meg. A hibakeresben felvesszük az ecx regisztert figyelt értéknek. A hibakeres sajátossága, hogy a regiszterek neveit a dollár prefixszel kell ellátni. run ni ni 50
stepi
display $ecx
A hibakövetést tovább könnyítend, beállítjuk, hogy a hibakeres minden lépés után display/i $eip jelezze ki a következ utasítást. A /i formátumkód azt jelzi, hogy utasítást íratunk ki, az eip pedig az a számunkra el nem érhet regiszter, amely az aktuális utasítás címét tartalmazza. Innen, folytatva a léptetést, megállapítjuk, hogy a paraméter megfelel, azonban a kiírás mégsem jó. Kilépünk a hibakeresbl, és ecx elvermelése után betoldjuk a következ sort. mov [adat], cl A hibakeresés kezdete után látszik, hogy most már valóban különböz karakterek jelennek meg, de ezek nem a 0, 1, ... számok. Kapcsoljuk folytonos megjelenítésre az adat címkét, hiszen ennek a tartalmát írjuk ki. A /c kapcsoló azt jelzi, hogy karakteresen jelenítünk meg. display/c adat quit
Egy id után észrevesszük, hogy a karakterek nem növekednek, hanem csökkennek, ezért át kell írnunk a main.ciklus-ban a dec ecx utasítást inc-re. Ezután még az is kiderül, hogy elfelejtettük, hogy a karakterkódolás szerint a számjegyek nem a nulla pozícióról indulnak, hanem 30h-ról, ezért a kiir függvényben ecx vermelése után még meg kell növelnünk cl-t a következ utasítással. add cl, 30h A program készen van.
44
Assembly programozás
Kitlei Róbert
Gyakran elforduló hibák Az alábbiakban következik néhány, zárthelyi dolgozatokban gyakran elforduló hiba leírása. Ha adható rövid példa, dlt betvel szedve egy-egy konkrét elfordulást mutatunk be, majd pedig egy konkrét javítást. Csak az assemblyhez közvetlenül kapcsolódó hibák szerepelnek, így pl. a kiolvashatatlan kézírás, a feladat félreértése, csak speciális esetekre megoldott feladat stb. nem. 1. Nem elssorban assembly vonatkozású, de a kiolvashatatlan kézírás következtében sokszor nem egyértelm, hogy melyik regiszterrl van szó, mennyi a leírt konstans értéke stb. 2. Szintaktikai hibák. a) Számábrázolás. i. Betvel kezdd hexadecimális szám elejérl lemarad a nulla. ii. Hexadecimális szám végérl lemarad a »h« bet (és az elején sincsen 0x). b) Utasítások operandusai közül lemarad a vessz. 3. Az adatok mérete nem megfelel. a) A regiszterek méretének eltévesztése. Az al regiszter 4 bitesként kezelése. b) Az utasítás operandusainak mérete nem egyezik meg (kivéve movsx és movzx, ahol éppen eltérnek kell lenniük). mov eax, bl mov eax, ebx vagy mov al, bl
c) Nem megfelel adathosszat használunk adatok definiálásához. Duplaszavas adatot szeretnénk definiálni, de csak szavas adatterületet foglalunk le. adat resw 1 adat resd 1
d) A memóriához való hozzáférés nem a megfelel adathosszúságban történik. Ritka esetekben szándékosan tehetünk ilyet, de általában ez hiba. Olyan címre, amelyen duplaszavas méret adat van eltárolva, byte hosszan írunk. adat mov dd [adat], 0 al mov [adat], eax
Természetesen az is helytelen, ha ugyanarról a címrl például szó hosszan próbálunk olvasni. mov ax, [adat] mov eax, [adat]
4. Adatábrázolás. a) Szám ábrázolásánál az (eljel nélkül ábrázolt) szám konstans és a számjegy karakter összekeverése. Pl. szövegesen leírt szám (számjegyeket tartalmazó string) konverziójánál minden karaktert elbb eljel nélkül ábrázolt számmá kell alakítani. Ezt legegyszerbben a 0 számjegy kódjának levonásával lehet elérni. Ez a módszer kihasználja, hogy a számjegyek karakterkódjai sorrendben találhatóak. 5. Szegmensek. a) A szegmenshatárok jelölése lemarad. b) Kódszegmensben adat, adatszegmensben kód szerepeltetése. Az elbbi ekvivalens azzal, mintha gépi kódot írnánk kézzel.
Kitlei Róbert
Assembly programozás
45
c) Az inicializált és inicializálatlan adatszegmens összekeverése (.data és .bss), a másikba való adatok szerepeltetése.
46
Assembly programozás
Kitlei Róbert
6. Utasítások operandusai. a) Adathossz explicit megadása hiányzik, amikor ez nem derül ki máshonnan, pl. kétoperandusú utasítás esetén a másik operandusból. mov [eax], 2 mov dword [eax], 2 vagy mov word [eax], 2 vagy mov byte [eax], 2
b) Nem megfelel számú vagy fajtájú operandus megadása utasításhoz. Az alábbiakban elkerültük a 3. fajtájú hibát, mivel megadtuk az adat hosszát (ami máshonnan nem derülne ki), azonban a div-nek nincsen konstans operandust fogadó változata. A javításhoz felhasználjuk bl regisztert. div byte 2 mov bl, 2 div bl
c) Túlságosan összetett számítási mveletek egy utasításon belül. Ezeket csak több utasítással lehet kiváltani, amihez szükség lehet további regiszterek felhasználására. Ebben az esetben a regiszter eredeti tartalmát a verembe menthetjük, vagy lefoglalhatunk egy tárterületet erre a célra. A cimke címke tartalma plusz egyet szeretnénk eax-be tölteni. mov eax, [cimke] + 1 mov eax, [címke] inc eax
Az ebx és ecx regiszterek összegét szeretnénk eax-be tölteni. mov eax, ebx + ecx mov eax, ebx add eax, ecx
Egy komplex indexelési mveletet szeretnénk végrehajtani. Az utasítás az eredeti formájában nem érvényes, a helyettesít kódban az egyszerség kedvéért felhasználjuk eax és edx regisztereket. Megoldható a feladat úgy is, hogy átmeneti tárolókat veszünk fel az inicializálatlan adatszegmensbe. cmp [esi-(ecx-4)*4], [esi-(ebx-1)*4] mov eax, ebx ; eax = ebx dec eax ; eax = ebx - 1 shr eax, 2 ; eax = ( ebx 1 ) * 4 sub eax, esi ; eax = ( ebx 1 ) * 4 - esi neg eax ; eax = esi - ( ebx 1 ) * 4 mov eax, [eax] ; eax = [esi - ( ebx 1 ) * 4] mov edx, ecx ; edx = ecx sub edx, 4 ; edx = edx - 4 shr edx, 2 ; edx = ( edx 4 ) * 4 sub edx, esi ; edx = ( edx 4 ) * 4 - esi neg edx ; edx = esi - ( edx 4 ) * 4 mov edx, [eax] ; edx = [esi - ( edx 4 ) * 4] cmp edx, eax ; edx = [esi - ( edx 4 ) * 4]
Kitlei Róbert
Assembly programozás
47
d) Adat és cím fogalmának összekeverése. i. Adat címe helyett csak az adat szerepel: hiányzik a szögletes zárójel. A cimke címke tartalmát szeretnénk eax-be tölteni. mov eax, cimke mov eax, [cimke]
ii. Adat helyett adat címe szerepel: feleslegesen megjelen szögletes zárójel. Elfordul, hogy ez a hiba párosul 3.c)-vel. e) A cél és a forrás összekeverése (az operandusok fordított sorrendben vannak leírva). Különösen gyakran fordul el, hogy konstans vagy címke az els operandus, amelyeknek futási idben nem lehet értéket adni. f) Indexelési hibák. i. Indexregiszter negatív eljellel. Ezt 3.c)-hez hasonlóan csak hosszabb kódrészlettel lehet kiváltani. ii. Adat hosszának figyelmen kívül hagyása indexeléskor. Duplaszó hosszú adatok tömbjének indexelésekor csak byte-onkénti indexelés. mov [bazis+ecx], eax mov [bazis+4*ecx], eax
iii. A little endian tárolási mód figyelmen kívül hagyása. 7. Programkonstrukciók. a) Elágazás ágaiban lemarad a kiugrás az elágazás végére. Ez többirányú elágazásra is vonatkozik. Az eax regisztert akarjuk nullára vagy egyre állítani annak függvényében, hogy az adat címkén található duplaszavas adat értéke egy-e. A kiugrás hiánya miatt az igaz ág végrehajtása eax nullára állítása után ráfut a másik ág kódjára is, ami elállítja eax-ot egyre. cmp jne mov .else_ag mov eax, 1 .else_ag mov eax, 1 .elagazas_vege b) Ciklusok. i. A ciklus inicializáló lépése bekerül a ciklusmagba. ii. A ciklus inicializáló lépése lemarad. iii. C stílusú for ciklusok. 1) A feltételvizsgálatnak a ciklus elején (közvetlenül a ciklus címkéje után) kell megtörténnie. 2) A ciklusváltozó léptetésének a ciklus végén (közvetlenül a ciklusmag elejére való visszaugrás eltt) kell megtörténnie. c) Elérhetetlen programkód. Mivel sosem fut le, felesleges létrehozni. d) Alprogramok. Az alprogramra lokális, a futási idej veremben elhelyezked változó helyett globális változó létrehozása az adatszegmensben. Ez nem feltétlenül hiba... 8. Egyes utasításokhoz köthet hibák. a) A not és a neg utasítás keverése. A neg számítása során lemarad a +1. 48
Assembly programozás
dword [adat], 1 .else_ag eax, 0
cmp dword [adat], 1 jne .else_ag mov eax, 0 jmp .elagazas_vege
Kitlei Róbert
b) Szorzás és osztás során a forrás- és célregiszterek eltévesztése. Helyes alkalmazáshoz lásd a mul, imul, div, idiv leírását és a könyv végén a táblázatot.
Kitlei Róbert
Assembly programozás
49
9. Megszakítások, alprogramok. a) A megszakítás meghívása lemarad. A paraméterek beállításán kívül szükség van az ,,int 80h" sorra is. b) Alprogram címkéje lemarad. Nélküle nem lehet (kényelmesen) meghívni a belépési pontot a kód más részeirl. c) Alprogram címkéje nem lehet lokális. Az alprogramra lokális címkéket érdemes létrehozni. d) Az alprogram bevezet és/vagy kilép kódrészlete lemarad. Kell tapasztalattal a kódrészletek egyszersíthetek, de az alprogram végén legalább egy visszatér ,,ret" utasításnak szerepelnie kell. e) Az alprogramok elején és végén a használt regiszterek nincsenek elmentve és visszatöltve. 10. Fájlok. Lemarad a fájlleíró eltárolása. Ha megnyitás után nem tároljuk el a fájlleírót a memóriában (lokális vagy globális változóként), akkor a továbbiakban nem tudjuk elérni a fájlt. 11. Verem. a) Nem megfelelen rendezett a verem. Általában: a push-ok és pop-ok nincsenek párban. b) A verembe tett és abból kivett adatok mérete nem egyezik. Az eax regiszter alsó szavát szeretnénk áttölteni bx-be, és ehhez a vermet próbáljuk felhasználni. Az eredeti kódrészlet ezt elvégzi, mivel a byte-sorrend fordul, azonban két byte-tal többet hagy a veremben, mint amennyi kezdetben benne volt. Ennek elkerülése érdekében a teljes ebx-be pop-olunk. Ha el akarjuk kerülni, hogy ebx fels felét is felülírjuk, akkor a pop után vissza is léptethetjük esp-t a kiinduló helyzetbe. push pop eax bx push eax pop ebx vagy push eax pop bx add esp, 2
c) Nemsztenderd hozzáférés a veremhez. A verem push-on, pop-on és alprogramokon belül ebp-n való kezelésén kívül más módszerrel nagyon oda kell figyelni ahhoz, hogy a verem szerkezete el ne romoljon. 12. Makrók. a) A futási és fordítási id fogalmának összekeverése. A makrók fordítási idben, az elfordítási fázis során alakítják át a forráskód szövegét, a regiszterek csak futási idben léteznek, az utasítások futási idben hatnak. mov eax, 1 mov ebx, 2 %assign ecx eax + ebx mov eax, 1 mov ebx, 2 mov ecx, eax add ecx, ebx
b) Makróra lokális címkék alkalmazásának hiánya. Ha globális címkéket alkalmazunk makrókban, akkor nem lehet ket több helyen kifejteni, mert a fordító hibát ad többször definiált név miatt. Makróra lokális címke esetén minden kifejtéskor új, egyedi név keletkezik. %macro faktorialis %assign fakt 1 %assign i 1 1 %macro faktorialis 1 %assign fakt 1 %assign %%i 1 %rep %0 1 %assign fakt %%i * fakt %endrep %endm
%rep %0 1 %assign fakt i * fakt %endrep %endm
50
Assembly programozás
Kitlei Róbert
A feladatok megoldásai Értékek ábrázolása 1. Mekkora értékek tárolhatóak egy bitvektorban? Add meg általánosan is! bitek száma számrendszer 8 16 32 4·n hexadecimális decimális hexadecimális decimális decimális hexadecimális decimális 2. Töltsd ki az üres cellákat a táblázatban! bitek száma 8 8 8 16 16 16 bináris
1100 1111 0110 1011 1110 1000 1001 0110 0001 1110 0010 0000 1010 0101 1101 0010 0011 0111
eljel nélkül
00 FF 80
eljelesen
7F
legkisebb érték legnagyobb érték legkisebb érték legnagyobb érték 0
00 00
255
FF FF
-128
80 00
+127
7F FF FF FF
0 0
00 00 ... 00 n számjegy
65 535
FF FF FF FF
-32 768
80 00 00 00
+32 767
7F FF FF FF
hexadecimális 00 00 00 00
4 294 967 295
FF ... FF n számjegy
-2 147 483 648 +2 147 483 647
80 00 ... 00 n számjegy 7F FF ... FF n számjegy
0
16n-1
-24n-1
+24n-1-1
decimális eljel nélküli 207 107 232 38 430 8357 53 815 eljeles -49 +107 -24 -27 106 +8357 -11 721
hexadecimális
CF 6B E8 96 1D 20 A5 D2 37
3. Végezd el az alábbi mveleteket! 1001 1101 and 1100 0111 1000 0101 0110 1110 or 1000 0000 1110 1110 0101 1010 xor 1111 0001 1010 1011
0101 0110 1101 11012 + 1C E316 = 0111 0011 1100 00002 = 29 63210 = 73 C016 16 53710 0000 0011 1010 11002 = 0011 1100 1110 11012 = 15 59710 = 3C ED16 54 3216 or 38 52910 = 0101 0110 1011 00112 = 22 19510 = 56 B316 4. Írd le a saját nevedet ASCII kódolással, ékezetek nélkül! K i t l e i
szóköz 20
5. Írd le a ,,Hello Vilag" szöveget ASCII kódolással! H e l l o V i l a g
R o b e r t
52 6F 62 65 72 74
sorvége 0A
szóköz 20
sorvége 0A
4B 69 74 6C 65 69
48 65 6C 6C 6F
56 69 6C 61 67
Kitlei Róbert
Assembly programozás
51
Utasítások 1. Add meg a regiszterek tartalmát minden utasítás után! A feladatot a jelölt pontokon is el lehet kezdeni.
reg. vagy mem. a. mov ebx, 1023 mov al, -100 movsx eax, bl b. mov eax, 1C34F6E2h ebx al eax ax ah al c. mov byte [0FAh], 01Ch [0xFA] mov byte [0FBh], 0x22 [0xFB] mov ax, [0FAh] d. mov eax, 89ABCDEFh mov ah, al mov al, 42 mov bl, al mov bh, al e. mov ax, 0FF42h mov bx, 0DCBAh sub bh, al xor ax, bx f. mov al, 01011010b or ah, 10101010b g. mov al, 120 not al neg al h. mov bh, 254 inc bh add bh, 1 i. mov cx, 0x01F0b mov eax, -128 mul cx imul cl ax ax ah al bl bx ax bx bh ax bx al ah al al al bh bh bh cx eax dx : ax ax decimális bináris (megfelel bithosszon)
0000 0000 0000 0000 0000 0011 1111 1111 1001 1100 1111 1111 1111 1111 1111 1111 1111 1111 1111 0110 1110 0010 1111 0110 1110 0010 0001 1100 0010 0010 0010 0010 0001 1100 1000 1001 1010 1011 1100 1101 1110 1111 1110 1111 0010 1010 0010 1010 0010 1010 0010 1010 1111 1111 0100 0010 1101 1100 1011 1010 1001 1010 0110 0101 1111 1000 1001 1010 1011 1010 0101 1010 1111 1010 0111 1000 1000 0111 0111 1001 1111 1110 1111 1111 0000 0000 0000 0001 1111 0000 1111 1111 1111 1111 1111 1111 1000 0000 0000 0000 0000 0000:1111 1000 0000 0000 0
eljel nélküli 1023 156 65 535 63 202 246 226 28 34 8732 239 42 42 10794 65 346 56 506 154 26 104 39 610 90 250 120 135 121 254 255 0 496 4 294 967 168 0 : 63 488 0
eljeles +1023 -100 -1 -2334 -10 -30 +28 +34 +8732 -17 +42 +42 +10 794 -190 -9 030 -102 26 104 -25 926 +90 -6 +120 -121 121 -2 -1 0 +496 -128 0 : -2048 0
hexadecimális
00 00 02 FF 9C FF FF FF FF F6E2 F6 E2 1C 22 221C
2 309 737 967 -1 985 229 329 89 AB CD EF
EF 2A 2A 2A 2A FF 42 DC BA 9A 65 F8 9A BA 5A FA 78 87 79 FE FF 00 01F0 FF FF FF F0 00 00:F8 00 0
2. a. Az x, y és z címen byte-os adatok vannak. Számítsd ki z-be ( not x | y ) & x értékét! mov al, [x] not al or al, [y] and al, [x] 52
Assembly programozás
2. b. Számítsd ki eax-be ( eax 1 ) * 2 értékét! dec eax shr eax, 1
Kitlei Róbert
3. Adj meg minél több utasítást, amely a. nullára állítja eax értékét! mov xor and eax, 0 eax, eax eax, 0 xor sub eax, eax eax, eax shr shl sal eax, 32 eax, 32 eax, 32
b. nem változtatja meg a regiszterek értékét (legfeljebb a jelzbitek regiszterének kivételével)! nop mov xchg add sub eax, al, eax, eax, eax al 0 0 bt and and or or xor eax, eax, eax, eax, eax, eax, 0 0xFFFFFFFF eax 0 eax 0 ror rol cmp eax, 0 eax, 0 eax, ebx
jmp .kovetkezo .kovetkezo
c. eax 2. bitjét egyesre állítja, a többit pedig változatlanul hagyja! or eax, 100b bts eax, 2
d. az ellentétére állítja ebx legalsó (0.) és legfels (31.) bitjét! xor ebx, 0x8001
e. al tartalmát egy elre adott értékrl egy másik adott értékre állítja! (Például 229-rl 33-ra.) mov al, 33 add al, 33-229 sub al, 229-33
A feladat megoldható még az xor al, eltérés utasítással, ahol az eltérést a két értékbl az xor mvelettel kaphatjuk meg, jelen esetben ez 1100 0100b. Speciális esetekben további utasításokat is lehet alkalmazni, például bitforgatást, regiszter nullázását stb. 4. Az x és y két címke, melyeken 32 bites adatokat tárolunk. a. Valósítsd meg az x := y értékadást! Közvetlenül egyik címrl a másikra másoló utasítás nincsen. mov eax, [x] mov [y], eax b. Cseréld meg x és y tartalmát! mov mov mov mov eax, edx, [x], [y], [x] [y] edx eax push dword [x] push dword [y] pop dword [x] pop dword [y] vagy push dword [x] pop dword [y]
vagy
Kitlei Róbert
Assembly programozás
53
5. Milyen byte-okat tartalmaz sorban az adatszegmens? section .data db db db db dw dd 55h 55h, 56h 'a', 0Ah "hello" 1234h 89ABCDEFh A byte-ok sorban, hexadecimális alakban: 55 55 56 61 0A 68 65 6C 6C 6F 34 12 00 EF CD AB 89.
6. Valósítsd meg az ismertetett utasításokkal az adc eax, ebx utasítást! Az adc az els operandushoz hozzáadja a másodikat (mint az add), és még ehhez az átviteli bitet (azaz hozzáad még egyet, ha az átviteli bit be van állítva, különben az eredmény a két operandus összege). jnc inc add jmp .nincs_carry eax eax, ebx .vege jnc .nincs_carry A feladat rövidebben is megolható. inc eax Ehhez felhasználjuk, hogy az egyik ág kódja posztfixe a másik ágának: a carry ág végén nem ugrunk el, .nincs_carry hanem hagyjuk, hogy a vezérlés add eax, ebx ráfusson a másik ág kódjára. Hasonló kód keletkezik, ha egy C programban a switch szerkezet egy ágát nem zárjuk le break utasítással.
.nincs_carry add eax, ebx .vege
7. Mennyi a kódrészletek végrehajtása után eax értéke? .a xor inc jmp dec mov .vege a. Ugyanannyi, mint a program elején, mert nem hajtunk végre olyan utasítást, amely megváltoztatná eax értékét. Az utolsó két utasítás elérhetetlen, mert eltte feltétel nélkül elugrunk a .vege címkére; az ugrás eltti két utasítás ebx értékét állítja egyre. b. A feltételvizsgálat szerint két eset lehetséges: vagy nulla volt kezdetben eax értéke, vagy sem. Amennyiben nulla volt, elugrunk a .vege címkére. Amennyiben nem nulla volt, folytatódik a végrehajtás, és a következ utasítás kinullázza eax-ot. Tehát a .vege címkére érve eax értéke nulla, bármelyik úton is érkezett ide a vezérlés. Innen még egyetlen utasítás van hátra, amely megnöveli eax értékét egyre. c. A kezdeti feltételvizsgálat akkor ugrik a .vege címkére, ha eax regiszter értéke megegyezik eax regiszterével. Ez mindig teljesül, ezért a következ két kódsor elérhetetlen, mert rögtön elugrunk. A regiszter értéke nem változik meg. ebx, ebx ebx .vege ebx eax, ebx .b cmp eax, 0 je near .vege mov .vege inc eax, eax jmp eax .vege .c .c cmp eax, eax je near .vege dec eax
54
Assembly programozás
Kitlei Róbert
8. Írj olyan kódrészletet, amely ecx-be meghatározza eax és ebx maximumát! A számok eljel nélkül vannak ábrázolva. cmp eax, ebx ja near .eax_nagyobb mov jmp ecx, ebx .vege ; eljel nélküli összehasonlítás ; itt biztosan ebx a nagyobb (vagy egyenlek) ; kiugrás az elágazásból
.eax_nagyobb mov ecx, eax .vege 9. Lineáris keresés: az adat címkétl kiindulva keresd meg az els olyan byte-ot, amely nullát tartalmaz, és ennek a címét add meg eax-ben! mov eax, adat ; az adat címke címét betöltjük eax-ba
.ciklus cmp byte [eax], 0 je near .vege inc jmp .vege eax .ciklus
; ha az eax címen található byte értéke nulla, ; kiugrunk a ciklusból ; különben a következ byte-ra lépünk ; ... és folytatjuk a keresést
10. Valósítsd meg assembly nyelven az alábbi C++ programrészletet! Az a és b változók típusa int. for ( int i = 0; i < 10; ++i ) mov ecx, 0 ; i-t ecx-ben tároljuk a += b;
.ciklus cmp ecx, 10 jnl near .vege mov add dec jmp .vege eax, [a] [b], eax ecx .ciklus
; eljeles vizsgálat, ha i >= 10, ciklus vége ; a += b; ; ++i
Kitlei Róbert
Assembly programozás
55
11. Az n paraméter a memóriában található egy duplaszavas változóban. Számítsd ki az eax regiszterbe a. az els n szám összegét! b. n! értékét! Az adatszegmensben tárolt n mindkét esetben a következképpen néz ki. section .data n dd 9 a mov eax, [n] mov ebx, 0 mov ecx, 0 ; ebx: részletösszeg ; ecx: számláló .ciklus add ebx, ecx inc ecx cmp ecx, eax ja .vege jmp .ciklus .vege b mov eax, 1 mov ebx, 1 .ciklus cmp ebx, [n] jg .vege mul ebx inc ebx jmp .ciklus
Amennyiben egy másik kódrészlet határozza meg n értékét számunkra, az inicializálatlan adatszegmensben is elhelyezhetjük. section .bss n resd 1
12. Adott egy duplaszavas értékeket tartalmazó láncolt lista címke. Add meg a hosszát! mov ebx, fejelem mov eax, 1 .ciklus cmp dword [ebx], 0 je .vege mov ebx, [ebx] inc eax jmp .ciklus .vege Ez a kódrészlet néggyel tölti fel eax-et, ha az alábbi adatokra hívjuk meg. elem2 dd dd fejelem dd dd elem4 dd dd elem3 dd dd elem3 3 elem2 150 0 365 elem4 8163
13. Adott a szoveg címkén egy szöveg, amelynek ismert a hossza. Fordítsd meg helyben! mov esi, szoveg mov edi, szoveg + hossz 1 ; szoveg + hossz már a szöveg utáni pozíció .ciklus cmp esi, edi jnb .vege mov mov mov mov al, [esi] ah, [edi] [esi], ah [edi], al
inc esi dec edi jmp .ciklus .vege 56
Assembly programozás
Kitlei Róbert
14. Adott három idpont: egy-egy óra és perc, amelyek duplaszavasan vannak eltárolva a memóriában. Az els két idpont között részleges napfogyatkozás következik be. A napfogyatkozás mértéke a félidig lineárisan növekszik, félidben pontosan 50%, onnantól lineárisan csökken. Add meg, hogy a harmadik idpontban (amelyrl feltételezhet, hogy az elz kett között helyezkedik el) mekkora volt egész százalékra kerekítve a napfogyatkozás mértéke! Tegyük fel, hogy az adatok a következképpen vannak eltárolva. section .data kezdet .ora .perc veg .ora .perc idopont .ora .perc dd dd dd dd dd dd 10 00 14 00 10 30 mov ebx, 60 mov sub mul add sub shr eax, eax, ebx eax, eax, eax, [veg.ora] [kezdet.ora] [veg.perc] [kezdet.perc] 1 ; eax = a teljes napfogyatkozás ; idtartama ; ecx = a teljes idtartam fele
mov ecx, eax mov sub mul add sub eax, eax, ebx eax, eax, [idopont.ora] [kezdet.ora]
[idopont.perc] [kezdet.perc] ; eax = a kezdettl eltelt id
mov ebx, ecx cmp ebx, eax ja .tovabb sub eax, ecx sub eax, ecx neg eax .tovabb mov ebx, 50 mul ebx div ecx ; ugrás, ha a félid eltt vagyunk ; félid után vagyunk ; eax = id a napfogy. végéig
; eax * 50 / (a teljes id fele) ; pontosan a kért eredményt adja
Kitlei Róbert
Assembly programozás
57
15. A teniszben az a játékos nyer meg egy ún. rövidített játékot, aki elször ér el legalább 7 pontot úgy, hogy legalább 2 ponttal vezet (azaz ha az állás 7-6, akkor még nem dlt el a rövidített játék, például a második játékos megszerezheti a következ három pontot, és akkor nyer 7-9-re). Egy tömbben adott, hogy melyik játékos nyerte meg a soron következ játékot. A tömb hossza elre nem ismert. Add meg, ki nyerte a rövidített játékot, és milyen arányban! Tegyük fel, hogy a következképpen adott a nyert játékok tömbje. section .data jatek db 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0
A következ kódrészlet kiszámítja al-be és ah-ba, melyik játékos hány játékot nyert. A program futása az .Anyert illetve a .Bnyert címkén folytatódik attól függen, melyik játékos nyerte a rövidített játékot. xor eax, eax xor ebx, ebx mov esi, jatek .ciklus cmp bl, 2 jnge .Anemvezet cmp al, 7 jae .Anyert ; annak ellenrzése, vezet-e a játékos legalább 2 ponttal ; ha igen, akkor nyert, ha elért legalább 7 pontot ; ah és al (a játékosok által nyert pontok) kinullázása ; bl (a játékosok által nyert pontok különbsége) kinullázása ; esi mutatja az aktuális pozíciónkat a tömbben
; itt következhetne egy jmp nincsnyertes utasítás, mert ha az egyik játékos vezet, akkor a ; másik biztosan nem, de az sem okoz problémát, ha a vezérlés ráfut a másik ellenrzésre .Anemvezet cmp bl, -2 jnle .nincsnyertes cmp ah, 7 jae .Bnyert .nincsnyertes add al, [esi] inc ah sub ah, [esi] mov bh, [esi] shl bh, 1 dec bh add bl, bh inc esi jmp .ciklus ; az elzhöz hasonló ellenrzés a másik játékosra
; ah és al (a játékosok által nyert pontok) állítása ; bh = +1 vagy -1 attól függen, melyik játékos nyerte a pontot ; bl (a játékosok által nyert pontok különbsége) állítása ; a mutató léptetése
58
Assembly programozás
Kitlei Róbert
Parancssori paraméterátadás, rendszerszolgáltatások 1. a. Írd ki a ,,Hello vilag!" szöveget betrl betre bvülve! Elször csak egy ,,H" bett írj ki és egy sorvégét, aztán a ,,He" szöveget és sorvégét stb. section .text global main main push ebp mov ebp, esp sub esp, 4 1. b. Hasonlóan bvül módon írd ki az els parancssori paramétert! section .text global main main push ebp mov ebp, esp ; [ebp-4] : ciklusszámláló sub esp, 4
; [ebp-4] : ciklusszámláló
mov [ebp-4], dword 0 ; kezdetben a szöveg nulladik pozícióján állunk .ciklus cmp dword [ebp-4], 11 je near .vege mov eax, [ebp-4] mov dl, [szoveg + eax] mov [kiir + eax], dl mov [kiir + eax + 1], byte 0xA ; a következ karakter és egy sorvége mov add mov mov mov int inc jmp .vege mov xor int edx, eax edx, 2 eax, 4 ebx, 1 ecx, kiir 0x80 dword [ebp-4] ; a következ karakter .ciklus eax, 1 ebx, ebx 0x80 ; a kiírás hossza ; +1 az indexelés miatt ; +1 a sorvége miatt
mov [ebp-4], dword 0 ; kezdetben a szöveg nulladik pozícióján állunk .ciklus mov ecx, [ebp+12] mov ecx, [ecx+4] mov eax, [ebp-4] mov dl, [ecx+eax] cmp dl, 0 je near .vege mov [kiir + eax], dl mov [kiir + eax + 1], byte 0xA ; a következ karakter és egy sorvége mov add mov mov mov int inc jmp .vege mov xor int edx, edx, eax, ebx, ecx, 0x80 eax 2 4 1 kiir ; a kiírás hossza
; a szöveg (argv[1]) ; a következ karakter
dword [ebp-4] ; a következ karakter .ciklus eax, 1 ebx, ebx 0x80
section .data szoveg db "Hello Vilag" section .data szoveg db "Hello Vilag"
section .bss kiir resb 12
section .bss kiir resb 12 59
Kitlei Róbert
Assembly programozás
2. a. i) Alkoss a saját (lefordított, futtatható) programkódját kiíró programot! Feltételezheted, hogy a program neve ismert. Használj 1024 bites puffert a fájl tartalmának beolvasása során! ii) Nehezítés: a program nevét a parancssori paraméterek közül kell kinyerned. b. i) Alkoss a saját forrásszövegét kiíró programot! Feltételezheted, hogy a program neve ismert. ii) Nehezítés: a program neve a futtatott fájl neve .asm kiterjesztéssel. Feltételezheted, hogy a fájl neve kevesebb, mint 256 karakter hosszú.
section .text global main main push mov mov mov mov int mov ebp ebp, section .text global main main push mov mov mov mov mov int mov ebp ebp, section .text global main main push mov mov mov mov int mov ebp ebp,
section .text global main main push ebp mov ebp,
esp [ebp+12] ; argv [esi]; argv[0] fajlnev
esp
esp
esp
eax, 5 ebx,fajlnev ecx, 0x80 644q
eax, 5 ebx,[ebp+12] ebx, [ebx] ecx, 644q 0x80 [leiro],eax
eax, 5 ebx,fajlnev ecx, 644q 0x80 [leiro],eax
mov mov mov
esi, esi, edi,
.masol cmp byte [esi], 0 je near.masolas.vege mov mov inc inc jmp al, [edi], esi edi .masol [esi] al
[leiro],eax
.beolvas mov eax, 3 mov ebx,[leiro] mov ecx, adat mov edx, 1024 int 0x80 cmp eax, 1024 jne near .vege mov mov mov mov int jmp .vege mov mov mov mov int mov mov int eax, ebx, ecx, edx, 0x80 4 1 adat 1024
.beolvas mov eax, 3 mov ebx,[leiro] mov ecx, adat mov edx, 1024 int 0x80 cmp eax, 1024 jne near .vege mov mov mov mov int jmp .vege mov mov mov mov int mov mov int eax, ebx, ecx, edx, 0x80 4 1 adat 1024
.beolvas mov eax, 3 mov ebx,[leiro] mov ecx, adat mov edx, 1024 int 0x80 cmp eax, 1024 jne near .vege mov mov mov mov int jmp .vege mov mov mov mov int mov mov int eax, ebx, ecx, edx, 0x80 4 1 adat 1024
.masolas.vege mov byte [edi], '.' mov byte [edi+1],'a' mov byte [edi+2],'s' mov byte [edi+3],'m' mov byte [edi+4], 0 mov mov mov int mov eax, ebx, ecx, 0x80 5 fajlnev 644q eax
[leiro],
.beolvas edx, eax, ebx, ecx, 0x80 eax, ebx, 0x80 eax 4 1 adat 1 0
.beolvas edx, eax, ebx, ecx, 0x80 eax, ebx, 0x80 eax 4 1 adat 1 0
.beolvas edx, eax, ebx, ecx, 0x80 eax, ebx, 0x80 eax 4 1 adat 1 0
.beolvas mov eax, mov ebx, mov ecx, mov edx, int 0x80 cmp eax, jne near mov mov mov mov int jmp .vege mov mov mov mov int mov mov int eax, ebx, ecx, edx, 0x80
3 [leiro] adat 1024 1024 .vege 4 1 adat 1024
.beolvas edx, eax, ebx, ecx, 0x80 eax, ebx, 0x80 eax 4 1 adat 1 0
section .data fajlnev db"a.out", 0 section .bss adat resb 1024 leiro resd 1 section .bss adat resb 1024 leiro resd 1
section .data fajlnev db "listazo2.asm", 0 section .bss adat resb 1024 leiro resd 1
section .bss adat resb leiro resd
1024 1
60
Assembly programozás
Kitlei Róbert
3. Írj olyan eljárást, amely egy duplaszón elfér, eljel nélküli egész számot ír ki a képernyre tízes számrendszerben! A számot paraméterként kapja meg az eljárás. A kiírandó string legyen lokális változó (fix hosszúságú ASCII karaktertömb); az eljárásnak ezt töltse fel, majd hívja meg a kiírást. kiir push mov sub mov mov mov sub mov cmp je near .ciklus cmp je near xor mov div add mov dec jmp .vege inc .kiir mov mov mov mov sub int mov pop ret ebp ebp, esp,
esp 12
; ezen a 12 byte-on alkotjuk meg a számot ; kezdetben a szám egy nulla karakterbl és egy sorvégébl áll ; edi a nulla karakteren áll ; eax az els paraméter ; ha a kapott paraméter nulla, akkor nullát kell kiírni
byte [ebp-1], 0xA byte [ebp-2], '0' edi, edi, eax, eax, .kiir eax, .vege edx, ecx, ecx dl, [edi], edi .ciklus edi eax, ebx, ecx, edx, edx, 0x80 esp, ebp 4 1 edi ebp ecx ebp ebp 2 [ebp+8] 0
0 edx 10 30h dl
; ha a számból fennmaradó kiírandó rész nulla, akkor készen ; vagyunk az átalakítással ; 32 bitesen osztunk, ehhez kinullázzuk edx:eax fels felét ; osztás tízzel, a maradék (a következ karakter) edx-ben ; jelenik meg; mivel értéke 0..9 közötti, ezért dl-ben elfér ; dl eddig szám volt, most számjeggyé alakítjuk ; ... és beírjuk az aktuális pozícióra ; a mutató léptetése
; a ciklusmag utolsó végrehajtása végén túlléptettük edi-t, ; ezt itt korrigáljuk
; kiírni az aktuális pozícióról fogunk ; ki kell írni az összes karaktert edi-tl ebp-ig
; visszatérés az alprogramból
Kitlei Róbert
Assembly programozás
61
Makrók 1. Írd meg az adc utasítást általánosan, kétparaméteres makróként! %macro adc 2 jnc %%nincs_carry inc %1 %%nincs_carry add %1, %2 %endm 2. ; ha nincs beállítva az átviteli bit, az utasítás úgy mködik, ; mint az összeadás ; különben eggyel meg kell növelni a regiszter értékét
/ / / + | | | | | \ \
\ v v v v v v v = = = = = = = = = o o | o o + | | | | | | | | V - - - - - - - V ~ - - - ~ X X | | | |
= = = = = = = = =
3. a. Készíts deriváló makrót. A makró tetszleges számú paramétert kaphat, amelyek egy polinom együtthatóit adják. A makró tegye a verembe sorban a polinom deriváltjának együtthatóit szavakként! A deriválás szabálya: (c·xn)' = c·n·xn-1 b. Készíts integráló makrót a deriváló makróhoz hasonlóan. A konstans tagot nem kell figyelembe venni. Feltehet, hogy minden kapott együttható egész szám. %macro %assign %rep %0 - 1 push %assign %rotate %endrep %endm derival %%i %0 1-* %macro %assign %rep %0 - 1 push %assign %rotate %endrep %endm integral 1-* %%i %0 + 1 dword ( %1 / %%i ) %%i ( %%i - 1 ) 1
dword ( %1 * %%i ) %%i ( %%i - 1 ) 1
62
Assembly programozás
Kitlei Róbert
4. Add meg, hogy a parancssori paraméterként kapott file a legvégén tartalmazza-e a saját nevét. A fájl egy sorvége karakterrel zárul, ezt ne vedd figyelembe.
section .text global main main push ebp mov ebp, esp mov esi, [ebp+12] mov esi, [esi+4] mov eax, -1 .hossz inc inc cmp jne eax esi byte [esi-1], 0 .hossz ; esi a fájlnév kezdetére mutat
; megszámoljuk, hány karakter hosszú a fájlnév
mov [hossz], eax mov mov mov mov mov int eax, ebx, ebx, ecx, edx, 0x80 5 [ebp+12] [ebx+4] 0 777q
section .bss ; megnyitjuk a fájlt ; eltároljuk a fájlleírót hossz resd 1 ; a fájl kiszámított hosszát ; itt tároljuk átmenetileg leiro resd 1 ; a fájl leírójának helye ; negatív irányban kell eltolni, a fájl végérl visszafelé fajlvege resb 16 ; a fájl végén lev sorvége figyelmen kívül hagyása ; ide olvassuk fel a fájl ; pozícionálás a szöveg kezdetére a fájl végén ; feltételezzük, hogy 16 ; karakternél nem hosszabb
mov [leiro], eax mov mov mov neg dec mov int mov mov mov mov int mov mov mov mov .vizsgal cmp ecx, 0 je .egyezik mov al, [esi] cmp al, [edi] jne .nemegyezik inc inc dec jmp esi edi ecx .vizsgal eax, ebx, ecx, ecx ecx edx, 0x80 eax, ebx, ecx, edx, 0x80 ecx, esi, esi, edi, 19 [leiro] [hossz] 2 3 [leiro] filevege [hossz] [hossz] [ebp+12] [esi+4] fajlvege
; beolvasás ; ecx a ciklusszámláló ; esi a fájlnév kezdetére mutat ; edi a fájl végérl származó, vizsgálandó szövegre ; mutat ; ha minden olvasott karakter egyezett, és már nincs ; több, akkor a szöveg megfelel ; a következ karakter vizsgálata; ha eltér, ; a szöveg nem megfelel ; ha egyezett a karakter, a következ vizsgálatával ; folytatjuk
Kitlei Róbert
Assembly programozás
63
Irodalom (1) http://www.intel.com/design/intarch/intel386/docs_386.htm Az Intel, az x86 architektúra megalkotóinak honlapja, azon belül a 80386-os processzor dokumentációs oldala. Innen elérhetek a Software Developer's Manual (Szoftverfejleszti kézikönyv) kötetei. Nagyon részletesen, több száz oldalon keresztül mutatja be az architektúra minden részletét. (2) http://developer.amd.com/documentation.aspx Az AMD, az x86 architektúrájú processzorok másik népszer gyártójának honlapja. Az Architecture Programmer's Manual (Architektúra-programozói kézikönyv) írja le a processzorok felépítését és programozását. Az általunk tárgyalt pontokon nem tér el az Intel architektúrától. (3) http://nasm.sourceforge.net/ A Netwide Assembler honlapja.
·
http://nasm.sourceforge.net/doc/nasmdoc0.html A Netwide Assembler on-line dokumentációjának tartalomjegyzéke.
·
http://nasm.sourceforge.net/doc/nasmdoc4.html A dokumentáció elfordítási fázisról (makrókról) szóló fejezete.
·
http://developer.apple.com/documentation/DeveloperTools/nasm/nasmdocb.html Az utasításokat bemutató függelék a dokumentációban. A munkafüzet írásakor a nasm hivatalos honlapján nem volt elérhet, pedig gyakorló assembly programozó számára a dokumentáció talán leghasznosabb része.
(4) http://asm.sourceforge.net/ Linux alatti assembly programozáshoz hasznos oldal. Régebben http://linuxassembly.org/ címen mködött.
64
Assembly programozás
Kitlei Róbert
utasítás mnemonikja
nop adatmozgatás mov movsx movzx xchg not logikai
operandusok nincs operandusa semmit sem csinál
*
mködés
A táblázatok csak a legfontosabb utasítás-operandus párokat tartalmazzák. Pontos információk az AMD és az Intel dokumentációiban találhatóak.
reg, reg, mem,
32/16
cél := forrás reg/mem cél := forrás eljeles/eljel nélküli kiterjesztéssel reg/mem cél := forrás, forrás := cél reg cél := cél bitenként negálva
8 32/16
reg/mem
bt bts btc btr r/m , r / k setfeltétel reg8/mem8 and or xor add sub inc dec neg mul imul div idiv léptetések cmp jmp jfeltétel call ret int push pop pushad popad r/m, konst/cl
* * *
átvitel := cél forrás-adik bitje, bit marad/0/1/fordul cél := 1, ha teljesül a feltétel, cél := 0, ha nem cél := cél és/vagy/kizáró vagy forrás bitenként cél := cél + forrás illetve cél := cél - forrás cél := cél + 1 illetve cél := cél - 1 cél := 0 - cél
aritmetikai
reg/mem
eljel nélküli eljeles eljel nélküli eljeles
ax := dx:ax := edx:eax :=
al ax eax
· forrás · forrás · forrás,
vagy vagy az op. méretétl függen vagy vagy
al := ax / forrás, maradék: ah ax := dx:ax / forrás, maradék: dx eax := edx:eax / forrás, maradék: edx
vezérlésátadás
külön táblázatban (a 2. operandus lehet a cl regiszter is) összehasonlítás, a jelzbitek beállítása ugrás feltétel nélkül vagy feltétellel; külön táblázatban alprogramhívás
címke
nincs operandusa visszatérés alprogramhívásból konst8 szoftveres kivétel kiváltása r32/16/m32/16/k32/16/8 érték verembe helyezése reg32/16/mem32/16 érték visszatöltése a verembl nincs operandusa eax, ecx, edx, ebx, esp, ebp, esi, edi verembe mentése nincs operandusa edi, esi, ebp, esp, ebx, edx, ecx, eax töltése a verembl
cél < forrás jb jl jnae jnge sar sal cél forrás jbe jle jna jng cél forrás jae jge ror rol jnb jnl cél > forrás ja jg
forgatás
átvitel 0
fels bit 0. bit
* Ezeknél az utasításoknál a reg/mem, reg/mem/konst operandusok megengedettek, a mem/mem kivételével.
feltételes ugrások nem eljeles eljeles igaz egyenl átvitel hamis
veremkezelés
jnbe jnle
je jz jne jnz jc jnc
eljel nélküli léptetés
eljeles léptetés
fels bit 0. bit
shr shl
31............16 15...8 7.....0
átvitel
0 átvitel
fels bit 0. bit
átvitel 0
ah ax eax
al
fels bit 0. bit
átvitel
fels bit 0. bit
átvitel
fels bit 0. bit
A fentiekben r = reg = regiszter, m = mem = memóriatartalom, k = konst = konstans. A fels indexben szerepl 8, 16 és 32 a méretet jelenti bitben.
Kitlei Róbert
Assembly programozás
65
Hasonló témájú dokumentumok

- 2009-10-06 23:37:12

- 2009-05-10 17:24:08

- 2009-01-06 15:09:07

- 2009-01-06 14:34:49

- 2009-01-06 15:13:54

- 2009-01-06 14:37:36

- 2007-11-28 17:41:12
A mások által feltöltött dokumentumokat értékelheted. Ha úgy ítéled meg, hogy a vizsgára való felkészülés szempontjából hasznos volt egy dokumentum, akkor adj rá sokcsillagos értékelést.
Ha hibákat tartalmaz, vagy egyéb probléma van vele, akkor keveset.
A dokumentumok sorrendje az értékelések alapján adódik. Ami fentebb van a listában, azt hasznosabbnak ítélték társaid. Az új dokumentumok pedig (értékelések hiányában) szintén a lista tetején kezdenek.
Hozzászólások
Ha észrevételed van egy dokumentummal kapcsolatban (például hibát találtál benne), akkor a Hozzászólások részben jelezheted. Az olyan jellegű kérdéseket mint pl.: A 2. feladat 4. sorából milyen átalakítással jutottunk az 5. sorban szereplő képlethez? - szintén ide érdemes írni
Egy tipp az oldalhoz! - Add hozzá azokat a tantárgyakat a saját tárgyakhoz, melyeket aktuálisan hallgatsz a félév során. Így megkapod mások üzeneteit akik tantárggyal kapcsolatban írnak, illetve Te magad is írhatsz ezzel kapcsolatban. Írhatsz naptári bejegyzést, kitöltheted a tantárgyi adatlapját és egy tárgy lapján látod azokat a hallgatókat akik szintén felvették ebben a félévben a tárgyat.