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 |
|
|
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 |
|
|
|
Pelda01 |
Ends |
;A szegmens vége. |
|
|
End Start |
;A program vége. |
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 |
|
|
|
Pelda02 |
Ends |
;A szegmens vége. |
|
|
End Start |
;A program vége. |
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 :" |
|
||
|
Pelda03 |
Ends |
;A szegmens vége. |
|
|
|
End Start |
;A program vége. |
|
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
bitre “0” ad, 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 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.