ASSEMBLY

Fischer Henrik

 

Az assembly programozási nyelv, a legelső programozási nyelvek között született az első mikroprocesszorokkal együtt. Assembly nyelven lehet:

 a leghatékonyabb,

 legkisebb,

 és leggyorsabb programokat írni,

 de a legmunkaigényesebb módon.

Az assembly nyelv közvetlenül a processzor gépi kódjához kapcsolódik, az assembly nyelv utasításai és a processzor utasításai között egyértelmű megfeleltetés van. Ilyen módon a programozó a processzor minden utasítását elérheti, de ez azt is jelenti, hogy egy adott assembly nyelven csak egy adott processzorhoz lehet programot írni, hiszen a különböző típusú processzoroknak eltérő utasításkészlete van. Minden processzortípusnak saját assembly nyelve van és az egyiken megírt programot más processzorok nem tudnak futtatni. Assembly nyelven a programozók mnemonikokat használnak a processzor utasításainak azonosítására. Pl.:

ADD az összeadás,
NOP a “ne csinálj semmit” utasítás.

A mnemonikokat könnyebb megjegyezni, mint az utasítások gépi kódját és a kész program olvashatóbb lesz, könnyebb a javítása, fejlesztése. Az assembly nyelv biztosítja a szimbólumok bevezetésének a lehetőségét is, ez azt jelenti, hogy a programozónak nem kell fejből tudnia, pontosan melyik memóriacímen is helyezkedik el egy bizonyos adat, elegendő azt az adatot ellátnia egy címkével (pl.: ADAT) és ezt a címkét használnia minden esetben, amikor erre a bizonyos adatra szüksége van.

Az assembly nyelv fordítóprogramja az assembler, az assembly nyelven megírt programot fordítja gépi kódra. A szimbólumokat az assembler alakítja tényleges memóriacímekké, és így pl.: az ADAT nevű címke a programban mindenhol lecserélődik majd egy valódi címre.

A gépi kód: A gépi kód azoknak a szabályoknak az összessége, utasítások sorozata, amelyet a CPU végre tud hajtani. Az ilyen gépi utasításokból felépülő programnyelv a gépi nyelv

A gépi nyelv: A gépi nyelv valójában egy alacsonyszintű géporientált programozási nyelv, mely a CPU utasításkészletének, a gépi kód szabályainak megfelelően közvetlenül végrehajtható programok írására alkalmas. Ez a számítógépek programozásának legalacsonyabb szintje.

Az alacsonyszintű programnyelvek: Az alacsonyszintű programnyelvek szintje a gépi kódon kívül magában foglalja a géporientált assembly szintű nyelveket, melyek gépi utasításokkal azaz mnemoniokkal dolgoznak. A MNEMONIK az utasítások angol elnevezésének kezdőbetűi.

A magas szintű programozási nyelvek:

A magas szintű programozási nyelvek lehetővé teszik, hogy a programozó a bináris számok halmazának, illetve a mnemonikok megadása helyett az adott programozási nyelv szabályainak megfelelően kommunikáljon a számítógéppel. A programozási nyelvnek kidolgozott elmélete van amit formális nyelvnek neveznek. A formális nyelvre jellemző, hogy mondatai kötött szabályok szerint épülnek fel, egyszerűek és egyértelműek. A definiálásához szintaxisra és szematika leírására van szükség.

Szintaxis: azoknak a szabályoknak az összessége, melynek alapján az adott nyelvhez tartozó helyes mondatok előállíthatók, azaz a program formális megjelenését határozza meg.

Szematika: adja meg a programok, illetve a nyelvi elemek tartalmi jelentését, vagyis az adott nyelv szabályainak összessége. Magas szintű nyelvek, pl.:

 FORTRAN Matematikai és műszaki feladatok programozására alkalmas.

 ALGOL Matematikai és műszaki feladatok programozására alkalmas.

 COBOL Általános üzleti célra orientált adatfeldolgozó programnyelv.

 BASIC Oktatási célokra alkalmas személyi számítógépek nyelve.

 PASCAL Alkalmas műszaki, matematikai, adatfeldolgozó feladatok programozására.

 dBase Adatbázis-kezelő rendszer.

 Clipper Adatbázis-kezelő rendszer.

 Fox Base Adatbázis-kezelő rendszer.

 C, C++: A "C" nyelv az assembly szintű programozás előnyeit megtartva, korszerű, optimális programok készíthetők a segítségével, alkalmas rendszerprogramok készítésére is.

A magas szintű nyelvek használata sok előnnyel jár az assembly nyelvhez képest. A legfontosabb előny, hogy a programok fejlesztésének ideje nagyságrendekkel rövidebb, hátránya a memória kihasználtsága. Egy azonos feladatmegoldásra írt program magas színtű nyelven kb. kétszer annyi memória helyet igényel, mint ha assembly nyelven írtuk volna.

Egy assembly nyelven megírt program, gyorsabb és kisebb lesz, mint ha magasszintű programozási nyelvben írtuk volna meg:

 egy assembly nyelven írt program mérete kb.: 5-6-szor kisebb, mint a Pascal vagy C nyelven írt programok méretéhez képest.

 egy BASIC-ben megírt program általában 10-15-ször több időt igényel ugyanahhoz a művelethez, mint egy assembly nyelven megírt.

Az assembly programozási nyelvvel való ismerkedésünket kezdjük először a számítógépek assembly színtű programozásával.

AZ ASSEMBLY PROGRAMOZÁS ALAPJAI

A programozáshoz nem elég csupán a nyelv ismerete ismernünk kell a számítógép belső felépítését (CPU, RAM, stb.) is. Az assembly programok írásához szükségünk van egy DOS formátumú szövegszerkesztőre, és egy fordítóprogramra (pl.: TASM), illetve a hozzá tartozó linkerre (TLINK), ami előállítja a futtatható programot. A kész szövegfájt (.ASM) első lépésben lefordítjuk egy object fájlra (.OBJ) és utána (.EXE, vagy .COM) futtatható állományt készítünk belőle a linker segítségével.

A regiszterek működése:

 Általános rendeltetésű regiszterek az AX, BX, CX, DX neveket viseli.

Egy ilyen regiszter nem más mint egy 16 bites adat tárolására alkalmas rekesz, ahová 0-65535-ig bármilyen számot írhatunk. Használhatjuk 16 ill. 2*8 bitesként is, mert egy ilyen rekesznek van alsó ill. felső része. Pl.: Az AX regiszternél AL (alsó) ill. AH (felső) része.

Decimális

Bineáris

Hexadecimális

AX

AH AL

AH AL

46

00000000 00101110b

00 2Eh

5

00000000 00000101b

00 05h

1649

00000110 01110001b

06 71h

65536

11111111 11111111b

0FF FFh

Alapértelmezés szerint decimális számokat használunk, ettől eltérő esetben jelölni kell a szám típusát, továbbá ha egy hexadecimális szám betűvel kezdődik, akkor elé egy vezető nullát kell tenni, azaz a hexadecimális számnak mindig számjeggyel kell kezdődnie.

Egy 16 bites számmal megcímezhető legnagyobb memória cím értéke 65535. Hogy ne csak 64 Kbyte legyen az elérhető, ezért találták ki a szegmens és indexregisztereket. Melynek a lényege, hogy a rendelkezésre álló memóriából kiválaszthatunk egy 64 Kbyte méretű szegmenst amit már képes kezelni a processzor.

 Szegmens regiszterek:

CS (Code Segment) kódszegmens
DS (Data Segment) adatszegmens
ES (Extra Segment) extraszegmens
SS (Stack Segment) veremszegmens

 Indexregiszterek:

SI (Source Index) forrásindex
DI (Destination Index) célindex

 További két 16 bites regiszter:

BP (Base Pointer) bázismutató
Flag regiszter

A program felépítésének szabályai az .EXE programnál:

Kód

Segment

 

assume CS:Kód, DS:Adat, SS:Stack

Start:

.

 

.

 

.

Kód

Ends

Adat

Segment

 

.

 

.

 

.

Adat

Ends

Stack

Segment

 

.

 

.

 

.

Stack

Ends

 

End Start

A program felépítésének szabályai a .COM programnál:

Kód

Segment

 

assume CS:Kód, DS:Kód

 

Org 100h

Start:

.

 

.

 

.

Kód

Ends

 

End Start

Az .EXE és a .COM program között a különbség, hogy .EXE bármilyen hosszú lehet, míg a .COM-nak bele kell férnie egy szegmensbe, azaz nem lehet 64 Kbyte-nál nagyobb. A Segment jelöli a szegmens kezdetét, melynek a neve az előtte álló címke, ami bármi lehet. A szegmens végét az Ends jelzi. Az assume szerepe, hogy a szegmensregiszterekbe a hozzá tartozó szegmenscímet töltse. Az .EXE programoknál az assume a DS regiszterbe nem az értéket tölti, amit később használni szeretnénk, ezért a programunkba ezt külön be kell állítani. Az SS regiszternek nem kötelező értéket adni tehát a címkét is elhagyhatjuk, és az assume sorból is törölhetjük. Ahogy a .COM program felépítésénél látjuk, ezenkívül a CS értéke megegyezhet DS értékével, ilyenkor ugyanaz lesz a kód illetve az adatszegmensünk. Az Org meghatározza a program kezdőcímét a szegmensen belül, ezt célszerű 100h-nak választani, mert az ez alatti memóriaterületen van az operációs rendszer programunkra vonatkozó paraméterei, és adatai.

Az assembly programnyelvben is van lehetőség megjegyzések (comment) elhelyezésére egy pontosvessző után

A mov utasítás segítségével adatokat mozgathatunk egy forrásból egy célba, pl.: így lehet értéket adni az egyes regisztereknek. A mov utasítást két paraméter követi, az első a cél majd vesszővel elválasztva a forrás. Ha egy címkét írunk az adat helyére, akkor a címke szegmenscímét fogja a regiszterbe írni. A mov utasítás segítségével lehetőségünk van egy regiszterbe egy számot, egy másik regiszterbe értéket, egy címke szegmens illetve offset (eltolás) címet vagy egy memóriarekesz tartalmát tölteni vagy fordítva.

Szintaktikai szabályok:

 A muv utasítás után 2 operandus közül legalább az egyiknek regiszternek kell lenni.
Pl.: mov ax,52h ;Az ax regiszterbe tölti a 52h (82) számot.

 Ha a memóriában levő adatra hivatkozunk, a címet mutató regiszter vagy címke mindig szögletes zárójelben van. Pl.: mov ax,[si]      ;Az ax regiszterbe tölti az indexregiszter által mutatott 16 bites értéket.

 Ha a címet nem regiszter hanem egy címke határozza meg akkor eléje kel írni az elérendő adat típusát (word ptr). Pl.: mov word ptr [címke],ax      ;Az ax tartalmát a címke által mutatott helyre írja.

 A két operandus típusának meg kell egyezni, pl.: ha a forrás 8 bites akkor a célnak is 8 bitesnek kell lennie.

 Hibaüzenet nélkül kilép a DOS-ba ha az ax regiszterbe 4c00h értéket írunk.

A karakteres képernyő felépítése:

Egy betű kiíratása a karakteres üzemmódba úgy történik, hogy a betű ASCII kódját és színét a képernyő-memória megfelelő helyére írjuk, és ennek a memóriának is van egy szegmenscíme. A képernyő bal fölső karakterpozíciója 0b800h, ha nem ebbe a sarokba akarunk írni, akkor a szegmenscímhez hozzá kell adni egy eltolási (offset) értéket.

Számítása: tudjuk, hogy egy karakterhez 2 byte tartozik, az első a karakter kódja, a második pedig a színkód. Megnézzük, hogy egy sorba hány betű fér el (80), ezt megszorozzuk 2-vel, így megkaptuk egy sor hosszát. Pl.: ha 7. sor 13. oszlopba akarunk írni, akkor annak címe 80 karakteres üzemmódban 7*80*2+13*2=1146=47Ah azaz a teljes cím 0b800h:47Ah. Ezután a kiszámított címre írjuk a kívánt adatot. Figyelembe kell venni, hogy a 16 bites regiszternek az alsó 8 bitje tárolódik előbb és utána a felső része, ezért a színkódot az ah a karakterkódot az al regiszterbe kell tenni, vagy bármelyik másikba de ilyen sorrendbe.

Színbeállításnál a színkód byte minden külön bitjének jelentése van:

0.bit: az előtér kék színösszetevője,
1.bit: az előtér zöld színösszetevője,
2.bit: az előtér piros színösszetevője,
3.bit: az előtér intenzitása,
4.bit: a háttér kék színösszetevője,
5.bit: a háttér zöld színösszetevője,
6.bit: a háttér piros színösszetevője,
7.bit: a villogás ki-bekapcsolása (a bit 1 értékénél villog).

A kívánt színt úgy tudjuk előállítani, hogy a magfelelő színeket összekeverjük. A 8 féle előtérszint kiegészíti egy intenzitás bit, amit ha 1 állapotba állítunk, akkor szín fényesebb lesz. A háttér színét egy villogás bittel egészítették ki, amit ha bekapcsolunk, a betű villogni fog.

A további assembly ismereteket példaprogramok segítségével tekintjük át.

PÉLDA 1

A példa egy "A" betűt ír ki fekete alapon fehér betűvel, a 12. sor (y) és 40. oszlopba (x).

Pelda01

Segment

;Szegmensdefiníció.

 

assume cs:Pelda01,ds:Pelda01

;A cs és ds regiszterek beállítása a szegmens elejére.

Start:

MOV ax,Pelda01

;A ds regiszter beállítása.

 

MOV ds,ax

 

 

MOV ax,0b800h

;A képernyő-memória szegmens-

 

MOV es,ax

;címét es regiszterbe tölti.

 

MOV al,byte ptr [KOORD_Y]

;Az al regiszterbe tölti a KOORD_Y címke alatt tárolt értéket.

 

MOV bl,160

;A bl-be 160-at tolt, mivel egy sor 160 byte.

 

MUL bl

;Az al érteket megszorozza bl érekével, és az
:eredményt ax-be kapjuk.

 

MOV di,ax

;Az így kapott eredményt di indexregiszterbe tölti.

 

MOV al,byte ptr [KOORD_X]

;A vízszintes koordináta értékét al regiszterbe tölti.

 

MOV bl,2

 

 

MUL bl

;és megszorozza 2-vel

 

ADD di,ax

;majd hozzáadja di indexregiszterhez.

 

MOV al,byte ptr [BETU]

;Az al-be a betű kódját,

 

MOV ah,byte ptr [SZIN]

; ah-ba pedig a színkódot tölti.

 

MOV es:[di],ax

;Az es:di által mutatott címre írja ax tartalmat, azaz

 

 

;a fekete alapon fehér "A" betűt.

 

MOV ax,4c00h

;Kilépés a DOS-ba.

 

INT 21h

 

KOORD_X: db 40
KOORD_Y: db 12
BETU: db "A"
SZIN: db 7

 

Pelda01

Ends

;A szegmens vége.

 

End Start

;A program vége.

 A program futtatása:

Az első lépésben beállítjuk a képernyő-memória szegmenscímét az es szegmensregiszterbe, de ezt közvetlen nem lehet állítani, ezért ax regiszteren keresztül tesszük meg. Ezután beállítjuk az offset (eltolás) értékét a di indexregiszterbe tesszük, az így kialakuló memóriacímre kiírjuk ax értékét és visszatérünk a DOS-hoz. A következő lépésben a program az általunk meghatározott xy koordinátára kiírja az "A" betűt, és az eltolás értékét a program számolja ki a megadott értékek alapján.

A mintaprogramban láthatjuk a memóriaváltozók használatát is. A memóriaváltozókat az adatok számára elkülönített részén kell elhelyezni, címkével azonosítjuk (KOORD_X: stb.), és meg kell jelölnünk az adat típusát is (db-byte, dw-word, dd-doubleword), majd el kell helyezni a tárolandó adatot. Ha egy betűt vagy szöveget idézőjelek közé tesszük, akkor annak ASCII kódja tárolódik, pl.: "A" helyén 65 lesz tárolva.

A mul utasítás segítségével a végrehajtás során, ha utána 8 bites adat áll (bl) az al regisztert szorozza meg a megadott regiszter tartalmával és az eredmény az ax-ben marad. Ha 16 bites adattal szorzunk (bx), az ax regiszter tartalma szorzódik és az eredményt a dx és ax regiszterekben kapjuk úgy, hogy a magasabb helyiértékű rész a dx-be az alacsonyabb pedig az ax-ben lesz.

Pl.: az ax regiszter tartalma 26e5h a bx tartalma 76ah akkor a mul bx végrehajtása során ax regiszterben 5dd2h a dx-ben 0120h értéket kapunk

A számmal való szorzás nem értelmezett, pl.: mul 3.

Az add összeadó utasítás hatására az első operandushoz adja a másodikat.

PÉLDA 2

A program egy szöveget ír ki "A kiíratandó szöveg", fekete alapon fehér betűvel, a 12. sor (y) és 30. oszloptól (x) kezdve.

Pelda02

Segment

;Szegmensdefiníció.

 

assume cs:Pelda02,ds:Pelda02

;A cs es ds regiszterek beállítása a szegmens elejére.

Start:

MOV ax,Pelda02

;A ds regiszter beállítása.

 

MOV ds,ax

 

 

MOV ax,0b800h

;A képernyő-memória szegmens

 

MOV es,ax

;címét es regiszterbe tölti.

 

MOV al,byte ptr [KOORD_Y]

;Az al regiszterbe tölti a KOORD_Y címke alatt tarolt értéket.

 

MOV bl,160

;A bl-be 160-at tölt, mivel egy sor 160 byte.

 

MUL bl

;Az al érteket megszorozza bl értékével.

 

MOV di,ax

;Az így kapott eredményt di indexregiszterbe tölti.

 

MOV al,byte ptr [KOORD_X]

;A vízszintes pozíció érteket al regiszterbe tölti

 

MOV bl,2

;és megszorozza 2-vel

 

MUL bl

 

 

ADD di,ax

;majd hozzáadja di-hez.

 

MOV ah,byte ptr [SZIN]

;Az ah-ba a színkódot tölti.

 

MOV si, offset SZOVEG

;Az si-be a szöveg offset címet tölti.

.1_Pelda02:

MOV al,[si]

;Az al regiszterbe az si álltál mutatott címen levő adatot tölti.

 

CMP al,255

;Az al értekét összehasonlítja 255-el.

 

JZ Vege

;Ha egyezik, ugrik a Vége címkéhez.

 

MOV es:[di],ax

;Az es:di által mutatott címre írja ax tartalmat,

 

 

;azaz a fekete alapon fehér "A" betűt.

 

ADD di,2

;A következő képernyő-pozíció

 

INC si

;A következő betű.

 

JMP .1_Pelda02

;Ugrik a .1_Pelda02 címkéhez.

Vege:

MOV ax,4c00h

;Kilépés a DOS-ba.

 

INT 21h

 

KOORD_X: db 30
KOORD_Y: db 12
SZOVEG: db "A kiíratandó szöveg",255
SZIN: db 7

 

Pelda02

Ends

;A szegmens vége.

 

End Start

;A program vége.

 A program futtatása:

A mintapéldában egy szöveg pozícionált kiíratását láthatjuk, itt nem elég csak ez karaktert kiolvasni, hanem egy meghatározott szöveget végig kell kiírni. Erre két megoldás van, az egyik hogy megszámoljuk, hogy hány betűt kívánunk kiíratni, és egy ciklus segítségével egyenként kiíratjuk, ennek a hátránya, ha megváltoztatjuk a szöveg hosszát, akkor a ciklust is meg kell változtatni. A másik módszer, hogy a szöveg végére elhelyezünk egy olyan kódot, amit a szövegben biztosan nem használunk (pl.: 255) és kiíratáskor figyeljük a kiíratandó karakter kódját, és ha nem 255 akkor kiírható, ha viszont 255 akkor vége a kiíratásnak.

A programban címkéket tartalmaz. A címkék használata hasonlít a memóriaváltozóknál megismert címkék használatához, csak ezeket a címkéket nem adattárolásra használjuk, hanem valamilyen ugráshoz használjuk (nem szekvenciális végrehajtás), pl.: jmp .1_Pelda02 sor. A szintaktika, hogy a címke után kettőspontot kell tenni (.1_Pelda02), kivéve a szegmens nevek ill. a memóriaváltozók, ha az adatszegmens nem egyezik meg a kódszegmenssel.

A jmp utasítás egy feltétel nélküli vezérlésátadó utasítás, ha a program ehhez a sorhoz ér, akkor a végrehajtás az utasítás után szereplő helyen fog folytatódni. Jelen esetben címkén, de lehet regiszter is si, di, bx, vagy a jmp far utasítás hatására egy másik szegmensbe is átugorhatunk, de csak az adott 64K-n belül lehet.

A karakter kód vizsgálatára a cmp utasítással kerül sor. A cal utasítás két érték összehasonlítására szolgál, az utasítás után a elsőször az adat áll és utána az amivel össze akarjuk hasonlítani. A művelet tulajdonképpen egy kivonás, a művelet elvégzése hatással van a carry és zero flag-ekre. A carry “0” ha az első számból kivonjuk a másodikat és nem negatív számot kapunk, ha “1” akkor az eredmény negatív azaz a második szám a nagyobb. A zero flag értéke “1” ha a kivonás eredménye nulla azaz a két szám azonos. A jz utasítás hatására a program csak akkor ugrik a megadott helyre, ha “z” bit értéke 1, ha a bit 0 értékét szeretnénk figyelni, akkor a jnz utasítást kell használni.

Az inc utasítás hatására az utána álló operandus értékét növeli eggyel, ha csökkenteni szeretnénk, akkor a dec utasítást kell használni.

PÉLDA 3

A program két bináris szám ÉS kapcsolatának eredményét írja ki a képernyőre.

Pelda03

Segment

;Szegmens-definíció

 

assume cs:Pelda03,ds:Pelda03

;A cs, ds beállítása

Start:

MOV ax,Pelda03

;A ds regiszter beállítása

 

MOV ds,ax

;a kód elejére

 

MOV ax,0b800h

;A képernyő-memória szegmens-

 

MOV es,ax

;címet es regiszterbe tölti.

 

MOV ax,3

;80*25 karakteres mod be-

 

INT 10h

;állítása, képernyőtörlés.

 

XOR di,di

;A di nullázása.

 

MOV si,offset SZOVEG1

;Az si mutatja a szöveg kezdő címét.

 

CALL Kiiro1

;Meghívja a Kiíró 1 eljárást.

 

MOV bl,byte ptr [SZAM1]

;A bl-ben a kiírandó szám van

 

CALL Kiiro2

;és ezt a Kiíró 2 rutin írja ki a képernyőre.

 

MOV di,160

;A következő szöveg kezdőcíme.

 

MOV si,offset SZOVEG2

;Ugyan az mint előbb.

 

CALL Kiiro1

 

 

MOV bl,byte ptr [SZAM2]

 

 

CALL Kiiro2

 

 

MOV di,320

 

 

MOV si,offset SZOVEG3

 

 

CALL Kiiro1

 

 

MOV bl,byte ptr [SZAM1]

;Az első számot bl regiszterbe teszi és

 

AND bl,byte ptr [SZAM2]

; végrehajtja a kijelölt műveletet a második számmal

 

CALL Kiiro2

; amit utána kiír a képernyőre. Itt kell a kívánt műveletet beállítani.

 

XOR ax,ax

;Billentyűvárás.

 

INT 16h

 

 

MOV ax,4c00h

;Kilépés a DOS-ba.

 

INT 21h

 

Kiiro1

Proc

;Kiíró 1 rutin kezdete.

 

MOV cx,16

;A szöveg 16 karakterből áll.

 

MOV ah,15

;Fekete alapon fehér szín.

.1_Kiiro1:

MOV al,[si]

;A kiíratandó betűt al regiszterbe tölti, majd

 

MOV es:[di],ax

;kiírja es:[di] által mutatott címre.

 

ADD di,2

;A következő karakterpozíció.

 

INC si

;A következő karakter

 

LOOP .1_Kiiro1

;Csökkenti cx értekét és ugrik a megadott helyre ha cx nem 0

 

RET

;Visszatérés a hívó programrészhez.

Kiiro1

Endp

;A rutin vége.

Kiiro2

Proc

;Kiíró 2 rutin kezdete.

 

ADD di,6

;Három karakterpozícióval arrébb lép.

 

MOV cx,8

;Az adat 8 bitből áll.

 

MOV ah,15

;Fekete alapon fehér szín.

.1_Kiiro2:

MOV al,"0"

;Az al regiszterbe a 0 ascii kódját tölti.

 

SHL bl,1

;Az adatot egyel balra lepteti, így a kicsorduló bit a carry flagbe kerül.

 

JNC .2_Kiiro2

;Ha ez a bit 0, akkor ugrás a .2_Kiíró címkehez.

 

MOV al,"1"

;Ha 1, akkor az al-be az 1 ascii kódját toltjuk.

.2_Kiiro2:

MOV es:[di],ax

;A számjegyet a képernyőre írjuk.

 

ADD di,2

;Egy hellyel arrébb.

 

LOOP .1_Kiiro2

;Ismétlés cx-nek megfelelően.

 

RET

;Visszatérés a rutinból.

Kiiro2

Endp

;A rutin vége.

SZOVEG1: db "Az első” byte :"
SZOVEG2: db "A második byte :"
SZOVEG3: db "Az eredmény :"
SZAM1: db 01011101b
SZAM2: db 10101011b

 

Pelda03

Ends

;A szegmens vége.

 

End Start

;A program vége.

 A program futtatása:

Ebben a programban már van ciklus, eljárások, feltételek, stb., és a regiszterek nullázására az xor művelet lett használva. Ha egy feladatra többször van szükség, akkor elég egyszer megírni, majd programból egy call (szubrutin hívása) utasítással végrehajtani, ez hasonlít jmp utasításra, de itt a gép megjegyzi a call utasítás címét a későbbi visszatéréshez. Erre mutat két példát a program, az eljárás (procedure) kezdetét egy Proc szó jelzi, az eljárás neve amivel hivatkozunk rá a Proc előtti címke. A rutint a címkenév és az Endp zárja le. Fontos sor a rutinba a ret utasítás, ez az utasítás jelenti a gépnek, hogy térjen vissza a call utáni sorra, ahonnan elindítottuk a rutint.

Az shl (SHift Left) utasítás hatására az adat balra tolódik úgy hogy a másik oldalról befutó bit értéke 0, és a kicsorduló bit a carry-be kerül.

A jnc (Jump if Not Carry) utasítás hatására ugrik, ha előjel nélkül nagyobb vagy egyenlő, a flag-eket nem befolyásolja.

Az xor (eXclsive OR) logikai kizáró vagy utasítás hasonlít az or működéséhez azzal a különbséggel, hogy itt két egyes találkozásakor is “0” ad. A programban az xor művelet azon tulajdonságát használjuk ki, hogy két azonos bitre0ad, ezért ha egy számot saját magával xor kapcsolatba hozunk akkor eredményül biztos, hogy nulla lesz. Ezt a trükköt a regiszterek nullázására szoktuk használni, mert a mov ax,0 utasítás sor a memóriában 3, míg az xor ax,ax utasítás csak 2 Byte-ot foglal el.

A loop utasítás segítségével cx hosszúságú ciklust szervezhetünk. A loop utasítás csökkenti cx értékét, és ha nem érte el a nullát, ugrik a megadott címre. A ciklus működése: egy regiszterbe beírjuk a végrehajtások számát, és a programrészlet végén csökkentjük a regiszter értékét, és ha még nem nulla, akkor megismételjük a programot mindaddig, míg a regiszter nulla nem lesz. A ciklus futásának számát a cx regiszterben kell megadni.

A 10h megszakítás a képernyőt kezeli, ha ah-ba “0” van, akkor a képernyő üzemmódját állítja be al értékének megfelelően, mely jelen esetben 80*25 karakteres mód.

A 16h megszakítás a billentyűzet kezelést végez, ha ah-ban “0” van, akkor a programot várakoztatjuk egy billentyű leütéséig.

PÉLDA 4

A program a számítógép által kezelt rendszeridőt írja ki.

Pelda04

Segment

;Szegmensdefiníció

 

assume cs:Pelda04,ds:Pelda04

;A cs, ds beállítása

Start:

MOV ax,Pelda04

;A ds regiszter beállítása

 

MOV ds,ax

;a kód elejére

 

MOV ax,0b800h

;A képernyő memória szegmens-

 

MOV es,ax

;címet es regiszterbe tölti.

 

MOV ax,3

;80*25 karakteres mod be-

 

INT 10h

;állítása, képernyőtörlés.

.1_Pelda04:

MOV ax,200h

;Az idő lekérdezése

 

INT 1ah

 

 

CALL Kiiro

;Az es kiírása.

 

MOV ax,100h

;Figyeli, hogy van-e

 

INT 16h

;lenyomott billentyű.

 

JZ .1_Pelda04

;Ha nincs, ugrik az elejére.

 

XOR ax,ax

;Ha van, akkor kiolvassa azt

 

INT 16h

 

 

MOV ax,4c00h

;és visszatér a DOS-hoz.

 

INT 21h

 

Kiiro

Proc

;A rutin kezdete.

 

MOV di,1670

;A képernyő közepe tája.

 

MOV ah,15

;Színbeállítás

 

MOV bx,cx

;Az óra, perc értéket áttölti bx-be.

 

CALL Kiiro2

;Kiírja az óra értéket.

 

ADD di,2

;Helyet hagy a kettőspontnak.

 

CALL Kiiro2

;Kiírja a percet.

 

ADD di,2

;A kettőspont helye.

 

MOV bh,dh

;A másodperc értékét bh-ba

 

CALL Kiiro2

;tölti és kiíratja.

 

MOV al,":"

;Az al regiszterbe a ":" jel

 

MOV es:[di-6],ax

;kódját teszi, és kiírja

 

MOV es:[di-12],ax

;azt a két megadott helyre.

 

RET

;Visszatérés a rutinból.

Kiiro

Endp

;A kiíró rutin vége.

Kiiro2

Proc

;A kiíró 2 rutin kezdete.

 

MOV cx,2

;Egyszerre 2 számjegyet

.1_Kiiro2:

PUSH cx

;írunk ki.

 

MOV cx,4

;Ami egyenként 4 bitből áll.

 

XOR al,al

;Az al nullázása.

.2_Kiiro2:

SHL bx,1

;A bx felső 4 bitjét átforgatja

 

RCL al,1

;al regiszterbe.

 

LOOP .2_Kiiro2

 

 

ADD al,48

;Átalakítja karakterkóddá

 

MOV es:[di],ax

;és kiírja a képernyőre.

 

ADD di,2

;A következő képhely.

 

POP cx

;A következő számjegy.

 

LOOP .1_Kiiro2

 

 

RET

;Visszatérés a rutinból.

Kiiro2

Endp

;Az eljárás vége.

Pelda04

Ends

;A szegmens vége.

 

End Start

;A program vége.

 A program futtatása:

A program a pontos időt írja ki a képernyőre, pl.: 12 óra 15 perc 1215h számként tárolódik. A számjegyeket számkarakterré kell átalakítani. a “0” számjegy ASCII kódja 48, ezt hozzáadva a számhoz és már írhatjuk ki a képernyőre. A ROM program elindítása után az eredményt, a ch-ban az órát, cl-ben a percet, dh-ban pedig a másodpercet kapjuk BCD formában.

A számítógép által kezelt óra szoftveres úton lekérdezhető, állítható. A programban ennek kezelésére az 1Ah BIOS program 02 funkciója ad lehetőséget. A hívás után az előbb említett regiszterekbe kapjuk az idő értékét, feladat hogy kiírjuk a képernyőre. A probléma az, hogy meg kell oldani, hogy az óra számoljon. Ha csak billentyűzet figyelést alkalmazunk akkor az óra nem jár, csak kiírja az aktuális értéket, és egy billentyű lenyomására kilépne. Egy jmp utasítással mindig vissza kel lépni a kiíratáshoz, akkor az óra számolni fog, de billentyűzetfigyelés nélkül nem lehet kilépni a programból. A megoldás egy másik fajta figyelés, a 16h rutinnak a 01 funkciója mely nem vár egy billentyű lenyomására, csak az aktuális állapotról ad információt az ax regiszteren illetve a flag-en keresztül. A z jelzőbit 1 értéke jelenti, hogy nincs lenyomott billentyű, tehát visszaugorhat az óralekérdezéshez. Amennyiben van, akkor azt ki kell olvasni a billentyűzet pufferből az eddig is használt rutinnal, de a különbség annyi, hogy nem vár, mert már van lenyomott billentyű, ha ezt a kiolvasást kihagynánk akkor kilépés után kiírná azt a betűt amit lenyomtunk.

A kiíratás egy eljárással van megoldva, amit egy másik kezel. A főprogram az időinformációk lehívását ill. a billentyűzet kezelését végzi. A megjelenítést egy rutin vezérli, hogy minden a megfelelő helyen legyen.

 Assembly utasítások

 Vissza az oldal elejére