Elég sokat beszéltünk már a bitekről de eddig sehol sem volt rájuk különösebben
szükségünk. A következőkben ismertetésre kerülő utasítások működésének
megértéséhez azonban elengedhetetlen a bitek fogalmának ismerete. Ugyanis a most
következő logikai műveletek hatása legegyszerűbben az egyes bitek viselkedésének
vizsgálatával érthető meg.
A legegyszerűbb ilyen művelet a bitek invertálása (ellenkezőjére való fordítása). Ezt a
not utasítással végezhetjük el.
A NOT MŰVELET MŰKÖDÉSE:
| Kiinduló érték: | 01001010 |
| A not művelet utáni érték: | 10110101 |
A példán jól látható, hogy az egyes helyiértékek tartalma az ellenkezőjére váltott.
Felhasználhatjuk ezt például 255-ből való kivonás helyett, mivel az eredmény ugyan az.
Ezen kívül még sok más helyen alkalmazható.
Egy az előzőhöz nagyon hasonló a neg művelet. Az eltérés csupán annyi, hogy míg
not un. egyes komplemenst számol addig ez a szám kettes komplemensét adja
eredményül, ami az eredeti érték -1-szerese, ezt legegyszerűbben úgy számolhatjuk ki,
ha invertáljuk a biteket és az eredményhez hozzáadunk egyet.
A NEG MŰVELET MŰKÖDÉSE:
| Kiinduló érték: | 01001010 |
| A neg művelet utáni érték: | 10110110 |
Ezt a tömörítő eljárásnál fogjuk használni, de erről majd ott. A műveletnek egyébként
negatív számok kezelésénél lenne szerepe, de ezzel nem foglalkozunk.
A következő logikai műveletekhez már két adatra lesz szükség, mivel az egyes bitek
találkozásától függ az eredmény értéke. Ezek az and, or illetve xor utasítások.
Az and (és) művelet során az eredményben csak ott lesz az eredmény adott bitje 1
értékű, ahol a kiinduló értékben mindkét adatban 1 az adott bit értéke.
AZ AND MŰVELET MŰKÖDÉSE:
| 1.adat: | 1100 |
| 2.adat: | 1010 |
| eredmény: | 1000 |
Az or (vagy) műveletnél minden bit 1 értékű az eredményben, ahol a forrás adatok közül bármelyikben 1 az adott bit értéke.
AZ OR MŰVELET MŰKÖDÉSE:
| 1.adat: | 1100 |
| 2.adat: | 1010 |
| eredmény: | 1110 |
A xor (kizáró vagy) utasítás hasonlít az or működéséhez, annyi különbséggel, hogy itt a két darab egyes találkozása is nullát ad eredményül.
AZ XOR MŰVELET MŰKÖDÉSE:
| 1.adat: | 1100 |
| 2.adat: | 1010 |
| eredmény: | 0110 |
Ezek kombinálásával bármilyen logikai művelet előállítható. Például két számot and
kapcsolatba hozunk egymással és az eredményt invertáljuk stb. A xor műveletnek
szokták kihasználni azt a tulajdonságát, hogy két azonos bitre mindig nullát ad. így ha
egy számot saját magával xor-olok, annak eredménye biztos, hogy nulla lesz. Ezt a
trükköt regiszterek nullázására szokták felhasználni, mivel a mov ax,0 utasítás sor a
memóriában 3, a xor ax,ax csak 2 Byte-ot foglal el. Ez hosszabb programoknál jelentős
lehet. Illetve az or, and utasításokat lehet például arra felhasználni, hogy egy regiszter
stb. tartalmáról megtudjunk pár dolgot, mivel az or illetve and művelet végrehajtásakor
a flag egyes bitjei az eredménynek megfelelően állnak be. Ha például ax regiszter értéke
nulla akkor az or ax,ax utasítássor végrehajtása után a z flag értéke 1 lesz. Ez a sor
szintén rövidebb a cmp ax,0 megoldásnál.
A következő művelet, ami ebbe a témakörbe tartozik, az a forgatás. Lehetőség van
ugyanis a byte, word tartalmának forgatására többféle módon. Ami közös mindegyik
megoldásnál, hogy a byte vagy word széléről kicsúszó bit mindig beíródik a carry flagbe.
A szó szerinti forgatást a ror (rotate right) illetve rol (rotate left) utasítások végzik. A
forgatáskor az utasítás után kell írni, amit forgatni akarunk (regiszter, memóriatartalom)
és egy vessző után, hogy mennyit. Ez a 8086 alapú gépeknél (XT) vagy 1 vagy cl. Utóbbi
esetben cl regiszterben megadott értékkel forgat. 80286-tól fölfelé megadható nagyobb
szám is, de ekkor a programszövegben jelölni kell a fordítónak, hogy a programot nem
XT-re írtuk. Ez úgy történik, hogy az első sorba egy .286 sort helyezünk el. Innen a
fordító tudni fogja, hogy a program 80286 utasítást is tartalmaz.
A ROR UTASÍTÁS MŰKÖDÉSE:
| forgatás előtt: | 01100101 |
| forgatás után: | 10110010 |
A ROL UTASÍTÁS MŰKÖDÉSE:
| forgatás előtt: | 01100101 |
| forgatás után: | 11001010 |
Tehát a byte forgatása során a kiforgó bit beíródik a carry flagbe és a byte másik
szélén befordul mind a rol mind a ror utasításnál.
Hasonló módon működik az rcr illetve rcl utasítások, de itt a forgatott adatot
megtoldja egy bittel a carry flag. Tehát mint a byte kilencedik bitje működik ugyanis a
kiforduló bit a carry flagbe kerül és a carry előző értéke fordul be a byte másik oldalán.
AZ RCR UTASÍTÁS MŰKÖDÉSE:
carry értéke forgatás előtt: 0
| forgatás előtt: | 01100101 |
| forgatás után: | 00110010 |
| forgatás előtt: | 01100101 |
| forgatás után: | 10110010 |
AZ RCL UTASÍTÁS MŰKÖDÉSE:
carry értéke forgatás előtt: 0
| forgatás előtt: | 01100101 |
| forgatás után: | 11001010 |
| forgatás előtt: | 01100101 |
| forgatás után: | 11001011 |
A harmadik forgatási lehetőség, amikor az adat kicsúszó bitje szintén a c flagbe íródik, de a másik oldalról becsúszó bit értéke minden esetben 0.
AZ SHR UTASÍTÁS MŰKÖDÉSE:
| forgatás előtt: | 01100101 |
| forgatás után: | 00110010 |
AZ SHL UTASÍTÁS MŰKÖDÉSE:
| forgatás előtt: | 01100101 |
| forgatás után: | 11001010 |
Egy byte illetve word forgatására még egy lehetőség van, amikor a jobbra forgatásnál a signum (előjel) flag értéke íródik az adat bal szélére. A jobb szélső bit forgatáskor szintén c-be íródik. A művelet fordítottja azonos az shl működésével.
A SAR UTASÍTÁS MŰKÖDÉSE:
signum értéke forgatás előtt: 0
| forgatás előtt: | 01100101 |
| forgatás után: | 00110010 |
| forgatás előtt: | 01100101 |
| forgatás után: | 10110010 |
AZ SAL UTASÍTÁS MŰKÖDÉSE:
| forgatás előtt: | 01100101 |
| forgatás után: | 11001010 |
Mindezeket a műveleteket kipróbálhatjuk a következő két mintaprogram segítségével,
ha a megfelelő helyre az általunk kipróbálni kívánt utasítást írjuk. Ennek helye a
szövegben külön jelölve van.
| Pelda05 | Segment | ;Szegmensdefinicio |
| assume cs:Pelda05,ds:Pelda05 | ;Cs, ds beallitasa | |
| Start: | mov ax,Pelda05 | ;Ds regiszter beallitasa |
| mov ds,ax | ;a kod elejere | |
| mov ax,0b800h | ;A kepernyomemoria szegmens- | |
| mov es,ax | ;cimet es regiszterbe tolti. | |
| mov ax,3 | ;80*25 karakteres mod be- | |
| int 10h | ;allitasa, kepernyotorles. | |
| xor di,di | ;Di nullazasa. | |
| mov si,offset SZOVEG1 | ;Si mutatja a szoveg kezdo- | |
| ;cimet. | ||
| call Kiiro1 | ;Meghivja a Kiiro1 eljarast. | |
| mov bl,byte ptr [SZAM1] | ;Bl-ben a kiirando szam van | |
| call Kiiro2 | ;es ezt a Kiiro2 rutin | |
| ;irja ki a kepernyore. | ||
| mov di,160 | ;A kovetkezo szoveg | |
| ;kezdocime. | ||
| mov si,offset SZOVEG2 | ;Ugyan az mint elobb. | |
| call Kiiro1 | ||
| mov bl,byte ptr [SZAM2] | ||
| call Kiiro2 | ||
| mov di,320 | ||
| mov si,offset SZOVEG3 | ||
| call Kiiro1 | ||
| mov bl,byte ptr [SZAM1] | ;Az elso szamot bl | |
| AND bl,byte ptr [SZAM2] | ;regiszterbe teszi es | |
| call Kiiro2 | ;vegrehajtja a kijelolt | |
| ;muveletet a masodik szammal | ||
| ;amit utanna kiir a | ||
| ;kepernyore. Itt kell a | ||
| ;kivant muveletet beallitani. | ||
| xor ax,ax | ;Billentyuvaras. | |
| int 16h | ||
| mov ax,4c00h | ;Kilepes a DOS-ba. | |
| int 21h | ||
| Kiiro1 | Proc | ;Kiiro1 rutin kezdete. |
| mov cx,16 | ;A szoveg 16 karakterbol all. | |
| mov ah,15 | ;Fekete alapon feher szin. | |
| .1_Kiiro1: | mov al,[si] | ;A kiiratando betut al |
| ;regiszterbe tolti, majd | ||
| mov es:[di],ax | ;kiirja es:[di] alltal | |
| ;mutatott cimre. | ||
| add di,2 | ;A kovetkezo karakterpozicio. | |
| inc si | ;A kovetkezo karakter | |
| loop .1_Kiiro1 | ;Csokkenti cx erteket es ugrik | |
| ;a megadott helyre ha cx nem 0 | ||
| ret | ;Visszateres a hivo | |
| ;programreszhez. | ||
| Kiiro1 | Endp | ;A rutin vege. |
| Kiiro2 | Proc | ;Kiiro2 rutin kezdete. |
| add di,6 | ;Harom karakterpozicioval | |
| ;arrebb lep. | ||
| mov cx,8 | ;Az adat 8 bitbol all. | |
| mov ah,15 | ;Fekete alapon feher szin. | |
| .1_Kiiro2: | mov al,"0" | ;Al regiszterbe a 0 ascii |
| ;kodjat tolti. | ||
| shl bl,1 | ;Az adatot egyel balra | |
| ;lepteti, igy a kicsordulo | ||
| ;bit a carry flagbe kerul. | ||
| jnc .2_Kiiro2 | ;Ha ez a bit 0, akkor ugras | |
| ;a .2_Kiiro cimkehez. | ||
| mov al,"1" | ;Ha 1, akkor az al-be az | |
| ;1 ascii kodjat toltjuk. | ||
| .2_Kiiro2: | mov es:[di],ax | ;A szamjegyet a kepernyore |
| ;irjuk. | ||
| add di,2 | ;Egy hellyel arrebb. | |
| loop .1_Kiiro2 | ;Ismetles cx-nek megfeleloen. | |
| ret | ;Visszateres a rutinbol. | |
| Kiiro2 | Endp | ;A rutin vege. |
| SZOVEG1: | db "Az elso byte :" | |
| SZOVEG2: | db "A masodik byte :" | |
| SZOVEG3: | db "Az eredmeny :" | |
| SZAM1: | db 01011101b | |
| SZAM2: | db 10101011b | |
| Pelda05 | Ends | ;A szegmens vege. |
| End Start | ;A program vege. |
Ez a program már sokkal összetettebb mint az előző négy. Ebben már megtalálhatók a
ciklusok, eljárások, feltételek stb. Mint az látható is a regiszterek nullázására itt már a
xor művelet lett használva.
Ha egy feladatra többször van szükségünk, akkor azt elég egyszer megírni, majd a
programból egy call utasítással végrehajtatni. Ez hasonlít a 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 is az 5.
program. Az eljárás (procedure) kezdetét egy Proc szó jelzi. Természetesen ahogy a
szegmenseknek, így az eljárásoknak is kell egy nevet adni, amivel később
hivatkozhatunk rá. Ez a név a Proc előtti címke. A rutint a címkenév és az Endp zárja.
Nagyon fontos sor a rutinunkban a ret. Ugyanis ez az utasítás jelenti a gépnek, hogy
térjen vissza a call utáni sorra, ahonnan elindították a rutint. Nagyon fontos dolog, hogy
ne próbáljunk meg eljárásból kilépni a DOS-ba, mert ez nagy valószínűséggel egy
lefagyást fog eredményezni. Ennek oka, hogy a számítógép kezel egy úgynevezett
stacket, ahová adatokat lehet elmenteni illetve onnan visszaolvasni. Ez a stack egy a
memóriában visszafelé növekvő terület, ha nem adunk meg az ss regiszternek külön
értéket, akkor a stack eleje a szegmensünk legvége lesz. Ha adatot mentünk ide, akkor
azt beírja és csökkenti a stack mutató értékét, ami mindig az utoljára beírt adatra mutat.
Ugyanígy kiolvasáskor is a legutoljára beirt számot kapjuk meg először és utána az
előzőt stb. Nos visszatérve a lefagyás okára, a program indításakor a visszatérési cím
beíródik a stackbe amit kilépéskor kiolvasva tudja, hova kell visszatérni. A call utasítás
is a stacket használja a visszatérési cím tárolására amit a ret-hez érve olvas ki és ugrik
a tárolt címre. Ha a rutinból próbálnánk meg kilépni, nem a DOS-hoz való visszatérés
címét olvasná ki a gép, hanem a call címét. Természetesen van megoldás, de ez egy
kicsit bonyolultabb, ugyanis megtehetjük, hogy kiolvassuk a stackből a call visszatérési
címét és ekkor a legutolsó tárolt adat a DOS-hoz való visszatérési címet fogja mutatni.
A másik fontos dolog ami megtalálható a programban az a ciklus. A ciklusok működése
azon az elven alapszik, hogy egy regiszterbe beírjuk a végrehajtások számát, és a
programrészlet végén csökkeltjük a regiszter értékét és ha még nem nulla, akkor
megismételjük a programot mindaddig míg a regiszter értéke nulla nem lesz. A PC-n ezt
a feladatot egyszerűen megoldhatjuk, mivel külön utasítás van erre a célra a loop. A
ciklus lefutásának számát cx regiszterben kell megadni és amikor a program a loop
utasítássorhoz ér, csökkenti cx értékét, és ha az még nem nulla, akkor ugrik a megadott
címre, ami hasonlóképpen a jmp-hez lehet címke illetve regiszter.
A programban a kilépésen kívül két ROM BIOS funkció is használva lett. Az 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. Jelen esetben a 80*25 karakteres módot. A 16h rutin a
billentyűzet kezelést végzi. Ha ah-ban nulla van, akkor a gép vár egy billentyű
lenyomására, és annak ascii kódját al illetve scan kódját ah regiszterben adja vissza. Itt
a visszaérkező adatot nem használjuk fel, mivel a dolog szerepe csak egy billentyűvárás,
hogy ne azonnal térjen vissza a DOS-hoz.
A program működését illetően a bináris számkiíratás ami új. Ezt úgy oldja meg, hogy
az adatot tartalmazó byte-ot eggyel balra forgatja, így abból a bal szélső bit értéke a
carry flagbe kerül. A művelet végrehajtása előtt al regiszterbe a nullás számjegy kódját
töltöttük be. Ha a forgatás során a c értéke 1 lenne, akkor al tartalmát az egyes
számjegy kódjára változtatjuk. Ha nulla, akkor átugorjuk a változtatást. Az így kialakult
számjegyet a már megszokott módon a képernyőre írjuk. Mindezt megismételjük az
összes bitre (azaz 8-szor).
| Pelda06 | Segment | ;Szegmensdefinicio |
| assume cs:Pelda06,ds:Pelda06 | ;Cs, ds beallitasa | |
| Start: | mov ax,Pelda06 | ;Ds regiszter beallitasa |
| mov ds,ax | ;a kod elejere | |
| mov ax,0b800h | ;A kepernyomemoria szegmens- | |
| mov es,ax | ;cimet es regiszterbe tolti. | |
| mov ax,3 | ;80*25 karakteres mod be- | |
| int 10h | ;allitasa, kepernyotorles. | |
| xor di,di | ;Di nullazasa. | |
| mov si,offset SZOVEG1 | ;Si mutatja a szoveg kezdo- | |
| ;cimet. | ||
| call Kiiro1 | ;Meghivja a Kiiro1 eljarast. | |
| mov bl,byte ptr [SZAM1] | ;Bl-ben a kiirando szam van | |
| call Kiiro2 | ;es ezt a Kiiro2 rutin | |
| ;irja ki a kepernyore. | ||
| mov di,160 | ;A kovetkezo szoveg | |
| ;kezdocime. | ||
| mov si,offset SZOVEG2 | ;Ugyan az mint elobb. | |
| call Kiiro1 | ||
| mov cl,1 | ;A forgatas erteket egyre | |
| ;allitja. | ||
| mov bl,byte ptr [SZAM1] | ;A szamot bl regiszterbe tolti | |
| ROR bl,cl | ;es vegrehajtja a kijelolt | |
| call Kiiro2 | ;muveletet, amit utanna kiir a | |
| ;kepernyore. Itt kell a | ||
| ;kivant muveletet beallitani. | ||
| xor ax,ax | ;Billentyuvaras. | |
| int 16h | ||
| mov ax,4c00h | ;Kilepes a DOS-ba. | |
| int 21h | ||
| Kiiro1 | Proc | ;Kiiro1 rutin kezdete. |
| mov cx,21 | ;A szoveg 21 karakterbol all. | |
| mov ah,15 | ;Fekete alapon feher szin. | |
| .1_Kiiro1: | mov al,[si] | ;A kiiratando betut al |
| ;regiszterbe tolti, majd | ||
| mov es:[di],ax | ;kiirja es:[di] alltal | |
| ;mutatott cimre. | ||
| add di,2 | ;A kovetkezo karakterpozicio. | |
| inc si | ;A kovetkezo karakter | |
| loop .1_Kiiro1 | ;Csokkenti cx erteket es ugrik | |
| ;a megadott helyre ha cx nem 0 | ||
| ret | ;Visszateres a hivo | |
| ;programreszhez. | ||
| Kiiro1 | Endp | ;A rutin vege. |
| Kiiro2 | Proc | ;Kiiro2 rutin kezdete. |
| add di,6 | ;Harom karakterpozicioval | |
| ;arrebb lep. | ||
| mov cx,8 | ;Az adat 8 bitbol all. | |
| mov ah,15 | ;Fekete alapon feher szin. | |
| .1_Kiiro2: | mov al,"0" | ;Al regiszterbe a 0 ascii |
| ;kodjat tolti. | ||
| shl bl,1 | ;Az adatot egyel balra | |
| ;lepteti, igy a kicsordulo | ||
| ;bit a carry flagbe kerul. | ||
| jnc .2_Kiiro2 | ;Ha ez a bit 0, akkor ugras | |
| ;a .2_Kiiro cimkehez. | ||
| mov al,"1" | ;Ha 1, akkor az al-be az | |
| ;1 ascii kodjat toltjuk. | ||
| .2_Kiiro2: | mov es:[di],ax | ;A szamjegyet a kepernyore |
| ;irjuk. | ||
| add di,2 | ;Egy hellyel arrebb. | |
| loop .1_Kiiro2 | ;Ismetles cx-nek megfeleloen. | |
| ret | ;Visszateres a rutinbol. | |
| Kiiro2 | Endp | ;A rutin vege. |
| SZOVEG1: | db "Az eredeti szam :" | |
| SZOVEG2: | db "A művelet eredménye :" | |
| SZAM1: | db 01011101b | |
| Pelda06 | Ends | ;A szegmens vege. |
| End Start | ;A program vege. |
Ez a program semmi újdonságot nem tartalmaz, mindössze nem két forrásadat lesz,
csak egy mivel a forgatáshoz csak egy adat szükséges.
Egy szintet vissza, vagy
vissza a főmenübe.