Hexadecimális számok kezelése


Az eddigi példákban megismerhettük a szövegkiíratás módjait illetve a bináris számok kiíratásának egy módjával. A következőkben ismertetésre kerül a számok hexadecimális, decimális kiíratása.

A hexa számok kiíratásánál problémát jelent, hogy a számjegyeken kívül A-F-ig betűket is tartalmazhat a szám. És a kilences szám és az A betű között vannak további karakterek, melyek kiíratása nem célravezető. A megoldás, hogy egy táblázatba helyezzük a lehetséges karaktereket és a számnak megfelelő jelet olvassuk ki innen és írjuk ki a képernyőre. A PC-n az ilyen táblázatkezelésre van egy speciális utasítás, ami a beállított táblázat al-edik elemét tölti al-be, ez az xlat. A táblázat offsetcímét (kezdőcímét a szegmens elejéhez képest) bx regiszterbe kell tenni a művelet végrehajtása előtt.

Pelda07 Segment ;Szegmensdefinicio
assume cs:Pelda07,ds:Pelda07 ;Cs, ds beallitasa
Start: mov ax,Pelda07 ;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 bx,offset HEXTABLE ;Bx regiszterbe a kovertalo
;tabla eltolasi erteket irja.
mov dx,word ptr [HEXSZAM] ;Dx regiszterbe tolti a
;kiirando szamot.
mov ah,15 ;A szamok szine fekete alapon
;fenyes feher.
mov cx,4 ;A szam negy szamjegybol all.
.1_Pelda07: push cx ;Cx erteket a verembe menti.
mov cx,4 ;Egy szamjegyet negy bit
;hataroz meg.
xor al,al ;Torli az al regisztert.
.2_Pelda07: shl dx,1 ;A dx regiszter felso negy
rcl al,1 ;bitjet al regiszterbe
loop .2_Pelda07 ;forgatjuk.
xlat ;A szamjegynek megfelelo
;karakterkodot tolti al
;regiszterbe
mov es:[di],ax ;es ezt kiirja a kepernyore.
add di,2 ;A kovetkezo irasi pozicio.
pop cx ;Cx elozo erteket kiolvassuk
;verembol.
loop .1_Pelda07 ;A kovetkezo szamjegy
;kiiratasa.
xor ax,ax ;Billentyuvaras.
int 16h
mov ax,4c00h ;Kilepes a DOS-ba
int 21h
HEXSZAM: dw 5b2eh ;A kiirando szam.
HEXTABLE: db "0123456789ABCDEF" ;Konvertalo tabla.
Pelda07 Ends ;A szegmens vege.
End Start ;A program vege.


Mint az látható, a program lelke a HEXTABLE címke alatt letárolt pár betű, ugyanis ha például a kiírandó számjegy decimális értéke 13 akkor a táblázat 13. elemét fogja kiírni a képernyőre, azaz egy D betűt. Ezt a műveletet hajtja végre az xlat utasítás. A programban egy 16 bites számot írunk ki. Egy hexadecimális számjegy 0-15-ig vehet fel értéket, amit 4 biten lehet ábrázolni. Tehát 4 egyenként 4 bites számjegyet kell kiírni a képernyőre. Ez két egymásba ágyazott ciklussal lett megoldva. Viszont ha egy regiszternek egymás után kétszer adunk értéket, (mivel a loop utasításhoz csak a cx regisztert lehet használni) akkor az a második értéket veszi fel és az elsőt elfelejti. Ellenben nekünk az első ciklusunk számolja a 4 karaktert és a második a 4 bitet. Tehát mindkettőre szükség van. A megoldás, hogy mielőtt a második ciklusnak értéket adnánk, ideiglenesen eltároljuk a cx regisztert a veremben, majd a második ciklus lefutása után kiolvassuk innen és az értékét csökkentve visszaugrunk az elmentésre mindaddig míg nem raktuk ki a teljes számot.

A stack (verem) működéséről már esett pár szó, de itt azért még egy kis részletezés, hogy mi is történik amikor a push utasítással elmentünk egy adatot vagy a pop segítségével kiolvasunk egyet. A stack, mint azt említettem az ss szegmensregiszter által meghatározott szegmens végétől kezdődően lefelé növekszik. A legutoljára beírt érték helyét az sp (stack pointer) regiszter mutatja. Ennek külső befolyásolása nem célszerű, csak feltétlenül szükséges esetben tegyük, mivel a gép adatmentéskor illetve kiolvasáskor automatikusan átírja az értékét. A továbbiakban egyébként ahol lehet kerüljük a veremműveletek használatát, mivel végrehajtása a többi utasításhoz képest meglehetősen lassú. Természetesen ez nem azt jelenti, hogy tilos használni, de ott ahol fontos a program gyorsasága, ott kerülendő.

A most következő mintaprogram a számok decimális formában való kiíratását mutatja be. melynek legegyszerűbb megoldása a tízzel való osztogatás mindaddig, míg a szám nem kisebb tíznél. Az egyes számjegyeket nem az osztás eredménye, hanem a maradék adja, mivel így bármekkora számot kiírhatunk, míg ha a másik lehetséges módszert alkalmazzuk, hogy elosztjuk a számot pl 1000-el, az eredményt kiírjuk, utána 100-al és így tovább, akkor a jelen esetben a legnagyobb kiírható szám a 9999.

A programban alkalmazott módszer hátránya, hogy a számjegyeket fordított sorrendben kapjuk meg, de a probléma könnyen megoldható, ha nem egyből a képernyőre írjuk az adatot, hanem letároljuk a memóriában és közben számoljuk a számjegyek számát. Ha végeztünk, fordított sorrendben kiírhatjuk a teljes számot.

A számjegyek számkarakterré való átalakítása itt sokkal egyszerűbb, mivel ha a 0 számjegy ascii kódja 48, ezért csak hozzáadunk a számhoz 48-at és már írhatjuk is a képernyőre.

Pelda08 Segment ;Szegmensdefinicio
assume cs:Pelda08,ds:Pelda08 ;Cs, ds beallitasa
Start: mov ax,Pelda08 ;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.
mov di,offset SZAMHELY ;Erre a cimre fogja letarolni
;a szamot a kiiras elott.
mov ax,word ptr [DECSZAM] ;A kirakando szam.
mov bx,10 ;Az osztas merteke.
xor cx,cx ;A szamlalo nullazasa.
.1_Pelda08: xor dx,dx ;A div utasitas a jelen
div bx ;esetben dx:ax regiszter
;tartalmat osztja, de
;szamunkra hasznos adat csak
;az ax regiszterben van,
;ezert a dx regisztert
;torolni kell.
mov [di],dl ;Az osztas maradekanak also
;byte-jat a memoriaba
;mentjuk.
inc cx ;A szamlalo novelese.
inc di ;A kovetkezo cimre irja a
;kovetkezo szamot.
or ax,ax ;Ax vizsgalata,
jnz .1_Pelda08 ;ha nem 0, ugras vissza.
mov si,di ;Si regiszterbe di-1-et
dec si ;totlunk, mivel ez az utolso
;ertekes szam.
xor di,di ;Di nullazasa.
mov ah,15 ;Szinbeallitas.
.2_Pelda08: mov al,[si] ;Al-be tolti az utoljara
;letarolt szamjegyet ami
;valojaban az elso.
add al,48 ;Ascii szamjegyye alakitja
mov es:[di],ax ;es kiirja a kepernyore.
add di,2 ;Kovetkezo pozicio.
dec si ;Elozo szamjegy.
loop .2_Pelda08 ;Ismetles a szamjegyek
;szamanak megfeleloen.
xor ax,ax ;Billentyuvaras.
int 16h
mov ax,4c00h ;Kilepes a DOS-ba.
int 21h
DECSZAM: dw 34576 ;Az abrazolando szam.
SZAMHELY: db ? ;A szam atmeneti tarolasara
;szolgalo hely
Pelda08 Ends ;Szegmens vege
End Start ;Program vege


A programban a div utasítás segítségével osztjuk el ax értékét tízzel, amit a bx-ben tároltunk. Azért kell 16 bites osztási műveletet használni, mert ha bl-t használnánk osztónak, az eredményt al, a maradékot ah regiszterben kapnánk és ha egy 2559-nél nagyobb számot osztanánk tízzel, a maradék nem férne el ah regiszterben. De így, hogy a dx:ax tartalmát osztjuk bx-el és dx értékét az osztás előtt nullára állítjuk, a maradékot dx-ben kapjuk, ami biztos, hogy el fog ott férni (655359-ig).

Egy szintet vissza, vagy vissza a főmenübe.