Pouze tento týden sleva až 80 % na e-learning týkající se C# .NET
Aktuálně: Postihly zákazy tvou profesi? Poptávka po ajťácích prudce roste, využij slevové akce 80% výuky zdarma!
https://www.itnetwork.cz/csharp

Lekce 2 - Assembler - Převod čísla na řetězec a naopak

V minulé lekci, Assembler - Zásobník, jsme si uvedli zásobník, jak se dá používat, a na co si dát pozor, když s ním pracujeme.

V dnešním ASM tutoriálu si předvedeme, jak převést binární číslo na řetězec pomocí primitivního algoritmu a rozebereme si, jak funguje. Vyzkoušíme si tím skoky v praxi.

Motivace

Někdy potřebujeme převést číslo na řetězec nebo opačně. Rozdíl mezi číslem a řetězcem z ohledu uložení v paměti by nám měl být jasný. Číslo je jedno číslo, které můžeme např. přičíst k jinému číslu. Řetězec je oproti tomu uložený jako sekvence znaků, tedy několik za sebou jdoucích čísel, každé označující kód nějakého znaku.

Funkce int_to_string

Začneme převodem z čísla na řetězec. Nejprve se podíváme na samotný kód, který si záhy vysvětlí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 se může zdát na první pohled poměrně složitý, ale hned se přesvědčíme o tom, že je opravdu jednoduchý. Mějme například číslo 158. Rozhodneme se, že si toto číslo převedeme na řetězec. Vložíme jej tedy do našeho algoritmu a ten začne pracovat.

Nejprve se uloží hodnoty všech registrů pomocí instrukce PUSHA a připraví se na potřebné hodnoty ty registry, které budeme používat. To jsou:

  • Registr CX se bude používat jako počítadlo, které nám v druhém cyklu řekne, z kolika znaků se číslo skládá.
  • Registr BX se bude používat pro dělení 10, tak získáme jednotlivá čísla, z kterých se bude řetězec skládat.
  • A do registru DI uložíme adresu, kde se nachází náš buffer (do něj se bude řetězec ukládat).
Tento výukový obsah pomáhají rozvíjet následující firmy, které dost možná hledají právě tebe!

Nyní se nacházíme v prvním cyklu. Zde se číslo 158 vydělí 10. Do registru DX se uloží zbytek po tomto dělení, tedy číslo 8. Všechna čísla postupně ukládáme na zásobník a pokračujeme, dokud není zbytek po dělení 0. To zajišťuje instrukce TEST. Při každém provedení cyklu se hodnota v registru CX zvýší o 1.

Přecházíme do druhého cyklu. Zde začneme všechna čísla ze zásobníku postupně vybírat. Ke každému číslu přičteme hodnotu 0x30, což je 48. Dostaneme se tak ke znakům čísel v ASCII tabulce. Každý, nyní už znak, přesuneme do buffer. Přesouvání probíhá, dokud není v registru CX hodnota 0, s každým cyklem se hodnota v něm sníží o 1.

Po přesunutí všech čísel obnovíme hodnoty všech registrů pomocí instrukce POPA a řetězec přesuneme do registru AX.

Funkce string_to_int

Nyní si ukážeme, jak to udělat obráceně. Někdy totiž potřebujeme převést řetězec na číslo, například uživatelský vstup. Opět si nejprve 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

Nejprve si opět ukládáme hodnoty všech registrů pomocí instrukce PUSHA a nastavujeme si potřebné registry:

  • Do registru SI přesuneme adresu vstupního řetězce z registru AX.
  • AX vynulujeme. Budeme do něj ukládat výstup.
  • Registr BX nastavíme na hodnotu 0x000a (10), budeme jím násobit.

Přejdeme do cyklu .next. Nejprve si uložíme hodnotu registru AX na zásobník a pomocí instrukce LODSB načteme do AL první znak ze vstupního řetězce. Ten přesuneme do registru CL a AX obnovíme. Pokud je v registru CL hodnota 0x00 (0), znamená to, že jsme na konci řetězce a skočíme na návěstí .return. V opačném případě odečteme z registru CL hodnotu 0x30 (48), abychom se dostali k číslům v ASCII tabulce.

Protože by mohl registr CH něco obsahovat, provedeme logický součin s hodnotou 0x00ff (255). Mohli bychom použít i XOR CH, CH. Registr AX vynásobíme 0x000a (10) a přičteme k němu hodnotu z registru CX.

Abychom si vysvětlili, proč násobíme AX hodnotou 0x000a (10), projdeme si cyklus ještě jednou. Opět použijeme číslo 158. V AX máme první číslici, 1. Nyní skočíme na návěstí .next a načteme další znak z řetězce do CL. Hodnota není 0x00 (0), pokračujeme dál. Z CX vytáhneme hodnotu 5 a přecházíme k násobení. AX se tedy vynásobí 0x000a (10), nyní je v něm hodnota 10. K té se přičte hodnota 5. To samé uděláme i s hodnotou 8. AX se opět vynásobí, bude obsahovat hodnotu 150 a k té přičteme hodnotu 8. Násobení nám tedy zajišťuje, že jednotlivé cifry budou na svých místech.

Nyní jsme na konci řetězce, skáčeme tedy na návěstí .return, do proměnné .buffer přesuneme hodnotu registru AX (158), obnovíme hodnoty všech registrů instrukcí POPA a do AX přesuneme hodnotu z proměnné .buffer.

Jednoduché, že?

V příští lekci, Assembler - Bitové operace, se budeme věnovat instrukcím pracujícím s bity.


 

Předchozí článek
Assembler - Zásobník
Všechny články v sekci
Programujeme operační systém v assembleru
Článek pro vás napsal Jakub Verner
Avatar
Jak se ti líbí článek?
1 hlasů
Autor se věnuje programování v Assembleru.
Aktivity (2)

 

 

Komentáře

Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zatím nikdo nevložil komentář - buď první!