IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

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 CX sa bude používať ako počítadlo, ktoré nám v druhom cykle povie, z koľkých znakov sa číslo skladá.
  • Register BX sa bude používať pre delenie 10, tak získame jednotlivé čísla, z ktorých sa bude reťazec skladať.
  • A do registra DI uloží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 SI presunieme adresu vstupného reťazca z registra AX.
  • AX vynulujeme. Budeme doň ukladať výstup.
  • Register BX nastavíme na hodnotu 0x000a (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.


 

Predchádzajúci článok
Assembler - Zásobník
Všetky články v sekcii
Programujeme operačný systém v assembleri
Preskočiť článok
(neodporúčame)
Assembler - Bitové operácie
Článok pre vás napísal Jakub Verner
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
Autor se věnuje programování v x86 Assembleru.
Aktivity