2. diel - Assembler - Prevod čísla na reťazec a naopak
V minulej lekcii, Assembler - Zásobník , sme si uviedli zásobník, ako sa dá používať, a na čo si dať pozor, keď s ním pracujeme.
V dnešnom ASM tutoriálu si predvedieme, ako previesť binárne číslo na reťazec pomocou primitívneho algoritmu a rozoberieme si, ako funguje. Vyskúšame si tým skoky v praxi.
Motivácia
Niekedy potrebujeme previesť číslo na reťazec alebo opačne. Rozdiel medzi číslom a reťazcom z ohľadu uloženia v pamäti by nám mal byť jasný. Číslo je jedno číslo, ktoré môžeme napr. Pripočítať k inému číslu. Reťazec je oproti tomu uložený ako sekvencie znakov, teda niekoľko za sebou idúcich čísel, každé označujúci kód nejakého znaku.
Funkcia int_to_string
Začneme prevodom z čísla na reťazec. Najprv sa pozrieme na samotný kód, ktorý si vzápätí vysvetlíme:
int_to_string: pusha xor cx, cx mov bx, 0x000a mov di, .buffer .push: xor dx, dx div bx inc cx push dx test ax, ax jnz .push .pop: pop dx add dl, 0x30 mov byte [di], dl inc di dec cx jnz .pop mov byte [di], 0x00 popa mov ax, .buffer ret .buffer times 7 db 0x00
Kód sa môže zdať na prvý pohľad pomerne zložitý, ale hneď sa
presvedčíme o tom, že je naozaj jednoduchý. Majme napríklad číslo
158. Rozhodneme sa, že si toto číslo prevedieme na reťazec.
Vložíme ho teda do nášho algoritmu a ten začne pracovať.
Najprv sa uloží hodnoty všetkých registrov pomocou inštrukcie
PUSHA a pripraví sa na potrebné hodnoty tie registre, ktoré
budeme používať. To sú:
- Register
CXsa bude používať ako počítadlo, ktoré nám v druhom cykle povie, z koľkých znakov sa číslo skladá. - Register
BXsa bude používať pre delenie10, tak získame jednotlivé čísla, z ktorých sa bude reťazec skladať. - A do registra
DIuložíme adresu, kde sa nachádza nášbuffer(do neho sa bude reťazec ukladať).
Teraz sa nachádzame v prvom cykle. Tu sa číslo 158 vydelí
10. Do registra DX sa uloží zvyšok po tomto
delení, teda číslo 8. Všetky čísla postupne ukladáme na
zásobník a pokračujeme, dokiaľ nie je zvyšok po delení 0. To
zaisťuje inštrukcie TEST. Pri každom vykonaní cyklu sa hodnota
v registri CX zvýši o 1.
Prechádzame do druhého cyklu. Tu začneme všetky čísla zo zásobníka
postupne vyberať. Ku každému číslu prirátame hodnotu 0x30,
čo je 48. Dostaneme sa tak k znakom čísel v ASCII tabuľke.
Každý, teraz už znak, presunieme do buffer. Presúvanie
prebieha, dokiaľ nie je v registri CX hodnota 0, s
každým cyklom sa hodnota v ňom zníži o 1.
Po presunutí všetkých čísel obnovíme hodnoty všetkých registrov
pomocou inštrukcie POPA a reťazec presunieme do registra
AX.
Funkcia string_to_int
Teraz si ukážeme, ako to urobiť obrátene. Niekedy totiž potrebujeme previesť reťazec na číslo, napríklad užívateľský vstup. Opäť si najprv ukážeme kód:
string_to_int: pusha mov si, ax xor ax, ax mov bx, 0x000a .next: push ax lodsb mov cl, al pop ax cmp cl, 0x00 jz .return sub cl, 0x30 and cx, 0x00ff mul bx add ax, cx jmp .next .return: mov [.buffer], ax popa mov ax, [.buffer] ret .buffer dw 0x0000
Najprv si opäť ukladáme hodnoty všetkých registrov pomocou inštrukcie
PUSHA a nastavujeme si potrebné registre:
- Do registra
SIpresunieme adresu vstupného reťazca z registraAX. AXvynulujeme. Budeme doň ukladať výstup.- Register
BXnastavíme na hodnotu0x000a(10), budeme ním násobiť.
Prejdeme do cyklu .next. Najprv si uložíme hodnotu registra
AX na zásobník a pomocou inštrukcie LODSB
načítame do AL prvý znak zo vstupného reťazca. Ten presunieme
do registra CL a AX obnovíme. Ak je v registri
CL hodnota 0x00 (0), znamená to, že sme
na konci reťazca a skočíme na návestí .return. V opačnom
prípade odpočítame z registra CL hodnotu 0x30
(48), aby sme sa dostali k číslam v ASCII tabuľke.
Pretože by mohol register CH niečo obsahovať, vykonáme
logický súčin s hodnotou 0x00ff (255). Mohli by sme
použiť aj XOR CH, CH. Register AX vynásobíme
0x000a (10) a pripočítame k nemu hodnotu z registra
CX.
Aby sme si vysvetlili, prečo násobíme AX hodnotou
0x000a (10), prejdeme si cyklus ešte raz. Opäť
použijeme číslo 158. V AX máme prvú číslicu,
1. Teraz skočíme na návestí .next a načítame
ďalší znak z reťazca do CL. Hodnota nie je 0x00
(0), pokračujeme ďalej. Z CX vytiahneme hodnotu
5 a prechádzame k násobenie. AX sa teda vynásobí
0x000a (10), teraz je v ňom hodnota 10.
K tej sa pripočíta hodnota 5. To isté urobíme aj s hodnotou
8. AX sa opäť vynásobí, bude obsahovať hodnotu
150 ak tej pripočítame hodnotu 8. Násobenie nám
teda zabezpečuje, že jednotlivé cifry budú na svojich miestach.
Teraz sme na konci reťazca, skáčeme teda na návestí
.return, do premennej .buffer presunieme hodnotu
registra AX (158), obnovíme hodnoty všetkých
registrov inštrukcií POPA a do AX presunieme hodnotu
z premennej .buffer.
Jednoduché, že?
V budúcej lekcii, Assembler - Bitové operácie , sa budeme venovať inštrukciám pracujúcim s bity.
