A táblázatok listája
2. Az assembly program felépítése
2.4. Adatok deklarálása
Ezek után térjünk rá az adatterület és a kódterület szétválasztásának technikájára. A „.data” direktívával azt közöljük a fordítóval, hogy minden egyes hivatkozás, deklaráció, amit ez után írunk, az adatterületen kerül létrehozásra. Tudnunk kell, hogy assembly program kód része sohasem keveredik össze a programban használt adatokkal, kódokban nem lehetnek adatok, adatokban pedig általában kódok. A kódterületet a kódszegmens, az adatterületet az adatszegmens regiszter jelöli ki, ezeket a futtató rendszer kezeli, nincs rá hatásunk. Persze ettől kellő hozzáértéssel el lehet térni, lesznek „trükkös” megoldásaink, amikor magunk, futási időben fogjuk írni a kódterületet, adatokat helyezünk el rajta, majd a korábban, mint tárolt „adatokra” később, mint végrehajtható
„kódokra” adjuk rá a vezérlést, de ezek nem szabványos megoldások, és tanácsos őket elkerülni, mert nehezen érthető és karbantartható programokat, rendszereket eredményeznek.
Az assembly nyelvben kétféle hivatkozás (szimbólum) van, az egyik a változó, a másik az utasításcímke. Az adatterületen változókat deklarálhatunk, ez egy kicsit eltér a magas szintű programnyelvekben megszokott deklarációtól, először a változó nevét adjuk meg, legyen ez pl. tesztadat (A kis/nagybetűre vigyázzunk!), a továbbiakban ezen a néven hivatkozhatunk rá.
Ezután az adattípus meghatározása következik. Sokféle adattípus szóba jöhet, ezek közül a legkisebb egység a byte. Tulajdonképpen itt nem a megszokott értelemben vett adattípust határozzuk meg, hanem fogjuk fel úgy, hogy azt mondjuk meg, hogy változónk egyedi példánya mekkora egységet (egységeket) foglal le a memóriában.
Ugyanebben a sorban kezdőértéket is adhatunk a változónknak. Tehát a
jelentése: változónk neve teszt lesz, ez egy 1 bájtos változó, 0x12 kezdőértékkel. Ekkor a fordító lefoglal a teszt adatunknak 1 bájtot, és a lefoglalt területre beteszi a 0x12 értéket. Legyen egy másik változónk is:
Ekkor, közvetlenül a teszt adat bájtja után a fordító egy szónyi (2 bájtnyi) területet foglal, és beleteszi az 0x5678-at. (Fordított sorrendben!!!). Ha a teszt adat kezdőcíme a 0100 lenne, akkor a tesz2 adat a 0101 címen helyezkedne el.
Módosítsunk egy kicsit a deklaráción:
A teszt adat most 4 darab bájtból áll a megfelelő értékekkel, és utána jön a teszt2 1 szavas (2 bájtos) adat. Ha a teszt adat kezdőcíme 0100, akkor a teszt2-é 0104. Pontosítsuk a fenti definíciónkat, a byte (word, dword,…) jelentése: változónk bájt(szó,duplaszó,…) méretű adatokból áll egészen addig, míg a következő szimbólum nem következik.
Lehetőség van arra is, hogy egy deklarációban egyszerre több egyedi példány számára foglaljunk le helyet úgy, hogy nem felsoroljuk őket hanem egy külön az erre a célra szolgáló direktívával határozzuk meg. Ez a DUP, melynek használata az őt követő zárójelek közötti inicializálási és az őt megelőző mennyiségi mutató
Térjünk vissza a programunk írására, tehát a következő sorunk a:
A fordító ennek hatására bájtos adatonként a karaktereknek a kódját (ASCII kód) helyezi el a memóriában, abban a sorrendben, ahogyan felsoroltuk, tehát először a ’E’ kódja, utána a ’z’ kódja, és így tovább. Az idézőjelbe tett sztring után vesszővel elválasztva további bájtokat is megadhatunk, esetünkben a 13, 10, 0-kat.
Tudnunk kell, hogy azokat a karaktersorozatokat, amiket 0-val zárunk, 0-val terminált sztringnek is értelmezhetjük, úgy gondolhatjuk, hogy egy összefüggő szövegfolyamról van szó, melynek a végét a 0 jelenti.
Magában az adatban nincs meghatározva a sztring hossza, azt nekünk kell meghatározni úgy, hogy végiglépegetünk ezen a karaktersorozaton és megkeressük a 0-t, ott van a sztringünk vége.
Nos, tehát létrehoztunk egy olyan változót, melynek neve tesztadat, típusa byte és a fenti karaktersorozatból áll.
2.4.2. Inicializálatlan adatterület
A következő lehetőség, hogy olyan adatokat hozunk létre, amelyeket nem inicializálunk, nem adunk neki kezdőértéket. Az előbbiekben a tesztadat pontosan megfelel annak a bájtos karaktersorozatnak, amit byte után írtunk.
Nem kötelező mindig előre meghatározni az adattartalmat, előfordul, hogy egy változónak csak a programunk futása során adunk értéket. Ekkor a „.data?” direktívát adjuk meg, a „?”-et az inicializálatlan változók jelölésére használjuk.
Amennyiben a változók után nem írunk kérdőjelet, a fordítás során hibaüzenetet kapunk.
Természetesen az inicializált és az inicializálatlan adatunkból is tetszőleges számú lehet.
A „.data” és a „.data?” direktíva bárhol elhelyezhető a programunkban, a fordító össze fogja azokat szerkeszteni egyetlen adatterületté, mely élesen különválik a kódterülettől.
2.4.3. Konstansok
2.4.3.1. Szimbolikus konstansok
Szimbolikus konstansokat programunk írása során azért használunk, hogy kódunkat jobban olvashatóvá tegyük.
Az „equ” vagy az „=” direktívával definiálhatóak, mely után értéket is rendelhetünk a szimbólumhoz. Az „=”
direktívával hozzárendelt értéket a kódunkban később felülírhatjuk, az „equ”-val adottat viszont nem. Amikor a kód írása során használjuk a konstansra hivatkozó címkét, a fordító egyszerűen behelyettesíti a megadott értéket a címke helyére. Szokás a konstansokat a forráskód áttekinthetősége miatt csupa nagybetűvel jelölni.
2.4.3.2. „.const” direktíva
A „.const” direktíva után deklarált változóink a futás során olyan területen jönnek létre, mely a program számára csak olvasási engedéllyel rendelkezik, ezért, ha megpróbáljuk kódunkban módosítani a változó értékét, az ismert
„Acces Violation” hibaüzenetet kapjuk.
2.5. Kódterület
Ha most leírjuk a „.code” direktívát, akkor elkezdhetjük írni a programkódokat, innentől a fordító kódokat fog értelmezni. Erre a területre már nem írhatunk változó deklarációt, mert akkor hibaüzenetet kapunk. Ha másik változót szeretnénk deklarálni, akkor ismét használnunk kell a „.data” vagy a „.data?” direktívát, deklarálni a változót, majd a „.code” direktívával visszatérni a kódterületre.
A következő sorban egy „start” nevű címkét fogunk használtunk, itt kezdjük el írni a kódunkat. Rögtön utána egy „nop” következik (No operation, üres utasítás). Ettől kezdve már valójában programozunk, vagyis ez a
„nop” már egy utasítás, mely a processzort egy művelet végrehajtására kéri fel, éppen arra utasítja, hogy ne csináljon semmit, hanem vegye a következő utasítást.
Használunk még egy „nop” utasítást, ennek az az oka, hogy a címkével jelölt sorokban nem tudunk ún.
töréspontot elhelyezni.
A töréspont egy adott pontja a programunknak, ahol megállíthatjuk a végrehajtást, és az eddigi futás eredményét ellenőrizhetjük, mik a regisztertartalmak, mi a változóink értéke, stb. (E nélkül ma már szinte lehetetlen fejleszteni).
Az al és a bl regiszterekbe bemozgattuk a korábban deklarált konstansokat (figyelem, csupa nagybetűvel, ahogyan deklaráltuk!), majd összeadtuk őket és az eredményt az al regiszterbe helyeztük el.
Írtunk még egy „inkey” utasítást is. Ennek hatására a program futása felfüggesztődik, míg a felhasználó a konzol ablakban meg nem nyom egy billentyűt. Az „inkey” utasítás tulajdonképpen egy makró. A makrók olyan kódrészletek, melyeket előre megírtak nekünk, és vagy paraméterezhetők, azaz megmondhatók, hogy milyen értékekkel alakítsuk a magunk igényeire, vagy direkt módon behelyettesítenek egy kódrészletet. Valójában egy kódrészlet teljes egészében idemásolódik, a lépésenkénti végrehajtás során látni fogjuk, hogy mi helyettesítődik be ennek az egyetlen sornak a helyébe.
Utolsó utasításunk egy „exit”, ami szintén egy makró, a program szabályos lezárását eredményezi.
Az assembler programot kötelezően az „end” direktívával kell lezárnunk. Az „end” direktíva után lévő utasítások nem hajtódnak végre. Az „end” direktíva paramétere programunk kezdőcímkéje, jelen esetben a
„start”. A futtató környezet ebből a paraméterből azonosítja a programunk belépési pontját, vagyis innen kezdi el futtatni a programot.