A programok másik fontos eleme a megjelenítések mellett a vezérlés. Az, hogy a
felhasználó hogyan tudja irányítani a program menetét, választani adott lehetőségek
közül. Az egyik legkulturáltabb megoldás a menüvezérlés, amikor a képernyőn felsorolt
lehetőségek közül úgy választhatunk, hogy például egy más háttérszínű sávval
mozoghatunk az egyes lehetőségek között, és az enter vagy más billentyű lenyomásával
lehet kiválasztani a megfelelő funkciót. Egy másik módszer, amikor a választás egy betű
lenyomásával történik, ilyen például az i/n választási lehetőség. Természetesen az
kevés, hogy kiírjuk a képernyőre a menüpontokat, és közöttük mozgunk, bár ez is
feladat, de a választás után el kell tudnunk dönteni, hogy melyik lehetőséget választottuk
és azt kell végrehajtani. Erre láthatunk egy példát a következő programban, ami egy
kicsit már hosszabb az eddigieknél és a bonyolultsága is nagyobb egy kicsit.
| Pelda10 | Segment | ;Szegmensdefinicio. |
| assume cs:Pelda10,ds:Pelda10 | ;Cs, ds beallitasa. | |
| Start: | mov ax,Pelda10 | ;Ds beallitasa a kod elejere. |
| mov ds,ax | ||
| mov ax,0b800h | ;Videomemoria szegmenscimet | |
| mov es,ax | ;es regiszterbe tolti. | |
| mov ax,3 | ;Kepernyotorles. | |
| int 10h | ||
| call Kiiro | ;A menupontok kiiratasa. | |
| .1_Pelda10: | call Csik | ;A mutato kirakasa. |
| .2_Pelda10: | mov ax,100h | ;Billentyuzet figyeles. |
| int 16h | ||
| jz .2_Pelda10 | ||
| xor ax,ax | ||
| int 16h | ||
| cmp ah,1ch | ;Enter figyelese | |
| jz Enter | ;Ugras ha az. | |
| cmp ah,48h | ;A felfele nyil figyelese. | |
| jnz .3_Pelda10 | ;Ha nem az akkor a kovetkezo | |
| ;vizsgalatra ugrik. | ||
| cmp byte ptr [MUTATO],1 | ;Ha a mutato erteke meg nem 1 | |
| jz .2_Pelda10 | ;akkor csokkenti a MUTATO | |
| dec byte ptr [MUTATO] | ;poziciojat egyel. | |
| jmp .1_Pelda10 | ;ujabb billentyu figyelese. | |
| .3_Pelda10: | cmp ah,50h | ;A lefele nyil figyelese, |
| jnz .2_Pelda10 | ;Ha nem az, vissza a | |
| ;billentyuzet figyelesre. | ||
| cmp byte ptr [MUTATO],5 | ;Ha a MUTATO meg nem az | |
| jz .2_Pelda10 | ;otodik helyen all, noveli | |
| inc byte ptr [MUTATO] | ;az erteket. | |
| jmp .1_Pelda10 | ;Vissza az figyeles elejere. | |
| Enter: | mov cl,byte ptr [MUTATO] | ;Cl ciklusvalltovoba a |
| ;MUTATO erteket tolti. | ||
| mov si,offset CIMEK | ;A CIMEK cimkenel tarolt | |
| .1_Enter: | mov bx,[si] | ;offsetcimek kozul azt |
| add si,2 | ;tolti a bx regiszterbe, | |
| loop .1_Enter | ;amelyiken a mutato allt. | |
| jmp bx | ;Ugrik a bx alltal mutatott | |
| ;programreszre. | ||
| .2_Enter: | mov si,offset KERDES | ;A KERDES alatti szoveg |
| call Kiiro2 | ;kiiratasa. | |
| .3_Enter: | mov ax,100h | ;Az igen/nem valasztasi |
| int 16h | ;lehetosegek vizsgalata. | |
| jz .3_Enter | ||
| xor ax,ax | ||
| int 16h | ||
| cmp ah,31h | ;Ha nem, ugras a kilepesre. | |
| jz .4_Enter | ||
| cmp ah,17h | ;Ha nem az igen, akkor | |
| ;ujabb billentyu beolvasasa, | ||
| ;mert a lenyomott gomb | ||
| jnz .3_Enter | ;ervenytelen. | |
| jmp Start | ;Ha az i lett lenyomva, | |
| ;ugrik a program elejere. | ||
| .4_Enter: | mov ax,4c00h | ;Kilepes a DOD-hoz. |
| int 21h | ||
| Menu1: | mov si,offset SZOVEG1 | ;Az egyes valasztasok |
| call Kiiro2 | ;eseten vegrehalytando | |
| jmp .2_Enter | ;programok. | |
| Menu2: | mov si,offset SZOVEG2 | |
| call Kiiro2 | ||
| jmp .2_Enter | ||
| Menu3: | mov si,offset SZOVEG3 | |
| call Kiiro2 | ||
| jmp .2_Enter | ||
| Menu4: | mov si,offset SZOVEG4 | |
| call Kiiro2 | ||
| jmp .2_Enter | ||
| Menu5: | mov si,offset SZOVEG5 | |
| call Kiiro2 | ||
| jmp .2_Enter | ||
| Csik | Proc | |
| mov al,byte ptr [MUTATO2] | ;A mutato regi erteket | |
| mov bl,160 | ;al, egy sor hosszat a bl | |
| ;regiszterbe tolti. | ||
| xor ah,ah | ||
| mul bl | ;Es kiszamolja a mutato | |
| add ax,1499 | ;kezdocimet. | |
| mov di,ax | ||
| mov cx,21 | ||
| mov al,7 | ;Fekete alapon feher szin. | |
| .1_Csik: | mov es:[di],al | ;A szininformaciok beirasa |
| add di,2 | ;a kepernyomemoriaba, | |
| loop .1_Csik | ;ezalltal torli az elozo | |
| ;csikot. | ||
| mov al,byte ptr [MUTATO] | ;Ugyan az, mint az elobb, | |
| mov byte ptr [MUTATO2],al | ;de mostmar az uj pozicioval | |
| mov bl,160 | ;es a kiamalt hatter szinnel | |
| xor ah,ah | ;rajzolja a csikot. | |
| mul bl | ||
| add ax,1499 | ||
| mov di,ax | ||
| mov cx,21 | ||
| mov al,47 | ||
| .2_Csik: | mov es:[di],al | |
| add di,2 | ||
| loop .2_Csik | ||
| ret | ||
| Csik | Endp | |
| Kiiro | Proc | |
| mov si,offset MENUK | ;A mar ismert szoveg | |
| mov ah,7 | ;kiirato rutin azzal a | |
| mov di,1660 | ;kiegeszitessel, hogy a | |
| ;0 kodra az eltarolt di | ||
| .1_Kiiro: | push di | ;erteket kiolvassa, hozzaad |
| ;160-at (igy sort emel), | ||
| .2_Kiiro: | mov al,[si] | ;majd ismet elmenti. |
| inc si | ;A 255 kod jelenti a szoveg | |
| cmp al,0 | ;veget es a rutinbol valo | |
| jz .3_Kiiro | ;visszaterest. | |
| cmp al,255 | ||
| jz .4_Kiiro | ||
| mov es:[di],ax | ||
| add di,2 | ||
| jmp .2_Kiiro | ||
| .3_Kiiro: | pop di | |
| add di,160 | ||
| jmp .1_Kiiro | ||
| .4_Kiiro: | pop di | |
| ret | ||
| Kiiro | Endp | |
| Kiiro2 | Proc | |
| mov al,[si] | ;Ez a kiiro csak az elso | |
| xor ah,ah | ;feleben kulonbozik az | |
| shl ax,1 | ;elozotol, ugyanis itt a | |
| mov di,ax | ;kiirando szovegy elso ket | |
| mov al,[si+1] | ;byte-ja a koordinataja, | |
| mov bl,160 | ;Amibol a program kiszamolja | |
| mul bl | ;az aktualis memoriacimet. | |
| add di,ax | ;Az eljaras a tovabbiakban | |
| add si,2 | ;azonos az elozovel, de itt | |
| mov ah,12 | ;nincs soremeles. | |
| .1_Kiiro2: | mov al,[si] | |
| inc si | ||
| cmp al,0 | ||
| jz .2_Kiiro2 | ||
| mov es:[di],ax | ||
| add di,2 | ||
| jmp .1_Kiiro2 | ||
| .2_Kiiro2: | ret | |
| Kiiro2 | Endp | |
| MUTATO: | db 1 | |
| MUTATO2: | db 1 | |
| MENUK: | db "Az elso menupont",0 | |
| db "A masodik menupont",0 | ||
| db "A harmadik menupont",0 | ||
| db "A negyedik menupont",0 | ||
| db "Az otodik menupont",255 | ||
| SZOVEG1: | db 22,5,"Az elso menupont lett kivalasztva.",0 | |
| SZOVEG2: | db 21,5,"A masodik menupont lett kivalasztva.",0 | |
| SZOVEG3: | db 21,5,"A harmadik menupont lett kivalasztva.",0 | |
| SZOVEG4: | db 21,5,"A negyedik menupont lett kivalasztva.",0 | |
| SZOVEG5: | db 22,5,"A otodik menupont lett kivalasztva.",0 | |
| KERDES: | db 28,20,"Akar ujra valasztani (i/n)",0 | |
| CIMEK: | dw offset Menu1 | |
| dw offset Menu2 | ||
| dw offset Menu3 | ||
| dw offset Menu4 | ||
| dw offset Menu5 | ||
| Pelda10 | Ends | |
| End Start |
Nos a program méretétől nem szabad megijedni, mert részegységeiben vizsgálva
nagyon sok minden ismerősnek tűnhet. Egyébként ez valójában egy parányi program, a
gyakorlatban ilyen rövid feladattal ritkán találkozunk.
Egy program működésének elemzésekor az egyik megközelítés az, amikor a
programot részegységeiben vizsgáljuk és csak az egyik programrészből a másikba átvitt
paramétereket kell számontartani a pontos működés megértéséhez. Ez a módszer itt is
célravezető.
Vegyük akkor sorra ennek a programnak a részegységeit: a legelső, a képernyő
beállítása a megfelelő üzemmódba, de ez már teljesen természetes. Ezután kiírjuk a
képernyőre az egyes menüpontokat. Nézzük, hogyan is történik ez: Ugyebár egy call
utasítással meghívjuk a Kiiro rutint, ami először is si indexregiszterbe tölti a kirakandó
szöveg kezdőcímét, ah-ba a színt és di-be a képernyőcímet, ahonnan a kiírást el kell
majd kezdeni. Itt ezt a program nem számolja, mivel ez egy fix érték ami nem változik,
ezért a címet közvetlenül a regiszterbe töltjük. De mivel a legritkább esetben áll egy
sorból egy menü, ezért a di értékét elmentjük a verembe, hogy később könnyebben
tudjuk kiszámolni a következő sor kezdőcímét. Ha mindezzel megvagyunk, következhet a
karakterek beolvasása a memóriából és képernyőre írása, hacsak nem vezérlőkód. A
rutin a 0-t és a 255-öt használja vezérlésre. A nulla hatására előveszi a veremből a di
elmentett értékét ami az éppen írt sor elejére mutat. Ha ehhez hozzáadunk 160-at,
éppen egy sorral lejjebb jutunk, így az egyes menüpontok pontosan egymás alatt lesznek
és ugyanabban az oszlopban fognak kezdődni. Természetesen a regiszter tartalmát ismét
el kell tárolni egy újabb sor címének kiszámításához. Ha nem nullával találkozik, hanem
255-el, akkor a rutin befejezésére ugrik ami abból áll, hogy a di tartalmát kiolvassa a
veremből, nehogy benne maradjon a már említett lefagyási lehetőségek miatt és egy ret
utasítással visszatér oda, ahonnan elindítottuk.
A program további teendője már többször fog ismétlődni, így ide egy címke is került,
amire később ugrani lehet. A legelső lépés itt, hogy a választást jelző csíkot kitesszük
valamelyik menüpontra (ez alapesetben a legelső, de ezen lehet változtatni). Ezt a Csik
nevű szubrutin végzi el, ami két majdnem egyforma részből áll. Az elsőben eltünteti a
képernyőn lévő csíkot, majd kirakja az újat. Ez a következőképpen történik: a MUTATO2
értékét ha megszorozzuk a sor hosszával (160) és hozzáadjuk az első menüsor címét
(160-at), akkor pontosan annak a menünek a helyét kapjuk, amelyiken éppen áll a
mutatónk. Azért kell 160-al kevesebbet hozzáadni, mert a mutató kezdőértéke 1 és ha
ezt megszorozzuk a sorhosszal, akkor a 160-at kapunk. A kezdőérték egyről való
indításának oka, hogy a későbbiek során amikor ezt az értéket egy ciklusban használjuk,
akkor ha cx=0 értékkel indul, akkor nem egyszer fog lefutni a ciklus, hanem 65536-szor
(ez a loop működéséből adódik). Ha kiszámoltuk a csík kezdőcímét, elkezdhetjük a csík
kirakását. Ezt a csík hosszának és színének beállításával kezdjük. Itt a szín az eredeti,
fekete hátterű a törlés céljából. Ezután a már kiszámolt memóriacímre, ami most nem
egy karakterhely, hanem egy színhely mert most a szöveget nem kívánjuk
megváltoztatni, csak a színét, kitesszük a mutatót jelképező csíkot. Tehát egy ciklussal
minden második helyre beírjuk a megadott színbyte-ot. Ha ezzel megvagyunk,
megismételjük a műveletet, de most már az új pozícióval és színnel. Majd a mutató
helyét a MUTATO2 változóba írjuk, hogy a következő elmozdításnál le tudjuk törölni a
jelenlegit. Ezután természetesen egy ret segítségével visszatér a rutinból. A most
következő BIOS rutin ellenőrzi, hogy van-e lenyomott billentyű. Ha van, akkor kiolvassa
azt és megvizsgálja, hogy a számunkra van-e jelentése. Ha nincs, akkor vissza a
figyeléshez. Ha a felfelé nyílat nyomtuk meg, akkor ellenőrizni kell, hogy a mutató nem
áll-e a legfelső soron, mert innen följebb már nem léphetünk. Ha nem ott állt, akkor a
mutató értékét csökkentjük eggyel. Hasonlóan a lefelé nyílnál is megvizsgáljuk a csík
helyét és ha lehet, növeljük a mutató értékét. Amennyiben a menüpont kiválasztására
használt enter-t nyomtuk le, akkor kilép a körből és az Enter címkénél folytatja a
program futását. Itt a cl regiszterbe tölti a MUTATO értékét, si-be pedig azt a címet,
ahonnan kezdve le lettek tárolva az esetlegesen indítható programok címei. Ez úgy
történt, hogy a CIMEK címke után wordös formában az egyes programok offsetcímei
kerültek letárolásra. Innen egy mov utasítással a bx regiszterbe töltjük először az első
címet, növeljük si értékét kettővel, majd a loop utasítás a cx-nek megfelelően
megismétli ezt a műveletet. Ha cx-ben 1 volt, akkor a ciklus nem kerül ismétlésre és a
bx regiszterben marad az első cím. Ha nem egy, akkor a cx-től függően a megfelelő
értéket kerül a bx-be. Ezzel megkerestük a kiválasztott menüpont által végrehajtandó
program címét. A feladat már csak annyi, hogy végrehajtsuk. Ezt egy jmp bx utasítással
tehetjük meg. Ezek a rutinok most csak annyit csinálnak, hogy si-be beállítják a
kiíratandó szöveg kezdőcímét (természetesen a menüponttól függően) és egy másik kiíró
rutin segítségével kiírják azt, majd visszaugranak a .2_Enter címkéhez. Az itt használt
szövegkirakó eljárás csak annyiban különbözik az előzőtől, hogy a szöveg elején található
2 byte, ami a kirakandó szöveg koordinátáját tartalmazza. Ebből a program a már
megszokott módon kiszámolja a képernyőcímet. A szöveg végét a 0 kód jelzi. Visszatérés
után egy kérdés kerül kirakásra, mégpedig, hogy akarunk-e újból választani. Itt figyeli,
ha az n gombot nyomtuk le, ugrik a kilépésre, ha az i gombot akkor elölről kezdi a
programot, egyébként újabb billentyű várása következik. A programban nem a
karakterek ascii kódja lett figyelve, hanem a scan kód mivel előfordulhat, hogy be van
nyomva a Caps Lock és ekkor az n billentyűt hiába figyeljük mivel leütésekor N kerülne
beolvasásra. Ellenben a scan kód a billentyűzet gombjait jelenti, nem pedig a rajtuk lévő
betűt.
Egy szintet vissza, vagy
vissza a főmenübe.