Zpět na seznam článků     Číst komentáře (1)     Verze pro tisk

Práce s textovými řetězci v assembleru (1)

Autor: Kub@z   
15.8.2004

Tento článek vám vysvětlí používání instrukcí pro práci s řetězci (STOSx, SCASx apod.)...


Možná si říkáte proč píši o něčem trochu pokročilejším a ne o základech assembleru, když tady zatím žádný článek na toto téma není - je to ze dvou důvodů: 1) často si tu někdo stěžuje, že sem umisťujeme jenom návody typu hello world, které jsou úplně všude, ale nic jiného sem nenapíšeme. 2) kdybych psal o základech assembleru, musel bych napsat i něco o jeho historii apod. a to se mi nechce, jelikož jí neznám a nechce se mi ji hledat :))) (možná někdy v budoucnu něco na toto téma napíšu).
  Tak... vraťme se k práci s textovými řetězci. Možná si říkáte, jak můžeme v assembleru pracovat s řetězci?? Vždyť je to tak primitivní jazyk... v něm přece řetězce nejsou. Odpověď zní: ale jsou. Samozřejmě, že práce s nimi bude o dost ložitější, než třeba práce se stringy v Pascalu, ale není to nic nepochopitelného a těžkého. Nejdřív si musíme vysvětlit, co to vlastně ten řetězec v assembleru je - je to posloupnost bytů, zakončená nulovým bytem - ne znakem "0", ale (Z jazyka C) znakem \0, tedy byte s hodnotou 0. Mluvíme zde vlastně o řetězích používaných v jazyce C, a jelikož je většina moderních operačních systémů napsaná v jazyce C (a tudíž používá tento typ řetězců), nemá smysl sy vymýšlet nějaké vlastní podivné struktury, které by byly v těchto systémech nepoužitelné. Zde je základní příklad programu v assembleru pracujícího z řetězcem:

org 100h
mov eax, retezec ; do eax offset retezce
zn:
 cmp byte [cs:eax], 0 ; není už nulový byte?
 je konec ; pokud ano, zkončíme
 inc eax ; pokud ne, eax = eax + 1
jmp zn ; a znovu opakujeme testování

konec:

int 20h

retezec db "Ahoj lidi",0

Tento program jenom problikne a nic nevypíše. Po skončení bude eax ukazovat na konec řetězce (na nulový byte). Jen pro upřesnění - tento program je určen pro zkompilování jako .com soubor, tudíž předpokládá, že segmentové registry budou zprávně nastaveny a je proto také možné ukončit ho pomocí int 20h. V tomto programu jsme zatím nepoužili instrukce pro práci s řetězci. Procesor totiž obsahuje instrukce, které nám práci s řetězci usnadní. Všechny tyto instrukce nemají žádný operand. Syntaxe je následující: "jmeno_instikce+velikost" Toto vypadá trochu zvláštně - hned vysvětlím. Jedna z těchto instrukcí se jmenuje stos. Chceme ji použít s velikostí jeden byte. Použijeme tedy instrukci stosb. Stejně tak je možné použít instrukci stosw a stosd. A co tedy tato instrikce dělá?

Instrukce STOSb/w/d

Instrukce stosb nahraje hodnotu v al na adresu [es:di] (nebo es:edi, zavisí na režimu procesoru - to platí u všech následujících instrukcí). StoswNahraje na tuto adresu hodnotu v ax a stosd hodnotu v eax. Po zkončení sníží nebo zvýší instrukce stos(x) hodnotu registru (e)di o 1, 2 nebo 4 byty (podle použité instrukce). To, jestli se hodnota bude zvyšovat, nebo snižovat závisí na nastavení příznaku směru (DF). Instrukci např. stosw lze tedy rozepsat jako:

mov [es:di], ax
add di, 2 ; případně sub di, 2 - podle df

Příznak směru a instrukce STD a CLD

Nebudu rozebírat podrobnosti, stačí pouze říci, že po innstrukci cld (nastaví df na 0) se bude hodnota di (příp. si) zvyšovat o danou hodnotu a po instrukci std se bude di (si) snižovat (což moc často nevyužijeme).

Instrukce LODSb/w/d

Instrukce lods pracuje opačně, než instrukce stos. Hodnotu do paměti neukládá, ale naopak ji z paměti nahrává (zase závisí na zadané velikosti, jestli se hodnota uloží do al, ax, nebo eax a o kolik se změní registr si). Ano - předchozí text je správně - registr si. Intrukce lods totiž nahrává z adresy [ds:(e)si]

Instrukce CMPSb/w/d

Instrukce cmps funguje stejně jako normální cmp, ovšem porovnává hodnoty na adresách [es:(e)di] a [ds:(e)si]. Po zkončení provede změnu registrů (e)di a (e)si podle příznaku směru a velikosti "operandu". Např. cmpsb by bylo možné rozepsat jako:

cmp byte [es:di], byte [ds:si]

Což nám samozřejmě kompilátor nedovolí, protože není možné porovnávat dvě hosnoty v paměti, ale museli bychom použít pomocný registr, čímž by se program celkem zkomplikoval, zpomalil, a zvětšil, kdežto instrukce cmps zabírá pouze jeden byte.

Instrukce SCASb/w/d

Instrukce funguje podobně jako cmps, ale [es:(e)di] s al/ax/eax, ne s hodnotou v paměti. Po zkončení samozřejmě upraví obsah registru (e)di

Instrukce MOVSb/w/d

Instrukce zkopíruje hodnotu z [ds:(e)si] na adresu [es:(e)di]. Např. movsw lze tedy rozepsat jako:

mov word [ds:si], word [es:di]

Což nám samozřejmě překladač nevezme a bylo by nutné použít pomocný registr. Nakonec samozřejmě zase upraví registry (e)si a (e)di

Instrukce REP, REPZ a REPNZ

Zde nám už končí řetězcové instrukce s b/w/d na konci :). Instrukce rep a repz funguje obdobně, jako instrukce loop a loopz. Na rozdíl od těchto instrukcí však neskáče stále dokola na zadanou adresu, ale jsou to tzv. prefixové instrukce - vykonávají instrukci napsanou za sebou. Tedy např. "rep mov ax,0" lze rozepsat pomocí loop jako:

z: mov ax, 0 loop z

Jenom pro vysvětlení - pokud nevíte, co dělá instrukce loop. Tato instrukce skočí na danou adresu (návěští), pokud není v cx hodnota 0 a po každém průběhu sníží cx o 1. Takže:

xor ax, ax ; ax = 0 (stejné jako mov ax, 0)
mov cx, 5  ; cyklus se zopakuje 5x
n: inc ax
loop n

Po skončení programu bude v ax hodnota 5. Instrukce repz (obdobně jako loopz) funguje stejně, jako rep, ale podmínka je nejen nenulový cx, ale zároveň nesmí být nastaven příznak ZF. Podmínku o příznaku ZF lze negovat přidáním "n" - repnz. Zjednodušeně - repz je něco jako:

n:
instrukce
cmp cx,0
jnz n

Výhodně lze instrukci rep použít například s instrukcemi movsb nebo stosb. Instrukce repz se zase používá s instrukcemi scasb nebo cmpsb.

Závěrem

Tyto instrukce velice usnadňují práci s řetězci. Užitečné rutiny pro práci s řetězci si ukážeme v dalším dílu tohoto seriálu. Jen na ukázku: Takto by vypadal program z úvodu přepsaný pomocí těchto instrukcí:

org 100h
mov di, retezec ; do di offset retezce
xor al, al ; vynulujeme al
xor ecx, ecx
dec ecx ; v ecx máme nyní 0FFFFFFFFh - největší možný počet opakování

cld

repnz scasb ; najdeme konec

int 20h

retezec db "Ahoj lidi",0

Po skonční programu bude v es:di adresa konce řetězce. Tento program je o dost kratší, než ten z úvodu, co říkáte? :) (no... přesněji - je kratší o 5 bytů, ale u delších programů se to projeví daleko více :))

Líbil se Vám článek?
Budeme potěšeni, pokud vás zaujme také reklamní nabídka

Social Bookmarking

     





Hodnocení/Hlasovalo: 2.56/9

1  2  3  4  5    
(známkování jako ve škole)