Archív

Archív pre kategóriu ‘Assembler x86’

Assembler – IF/THEN ala CMP, JMP a JX

September 6th, 2009 admin Žiadne komentáre

Inštrukcie CMP a TEST

Porovnanie dvoch operandov vykonávajú inštrukcie CMP a TEST. Ako operandy môžeme použiť registre alebo operand uložený v pamäti v známych veľkostiach 8, 16 a 32 bitov.
Názov inštrukcie CMP je odvodený z anglického slova compare (porovnaj). Inštrukcia je implementovaná podobne ako inštrukcia SUB. Operand o2 sa odpočíta od operanda ol. Výsledok sa nikam neuloží, zmenia sa len príznaky v registri príznakov. Inštrukciu CMP môžeme použiť na porovnanie celých čísel bez znamienka aj so znamienkom.
Inštrukcia TEST pracuje podobne, miesto rozdielu operandov sa vykoná ich bitový súčin (AND). Výsledkom sú novo nastavené príznaky v registri príznakov. Inštrukciu TEST používame pre testovanie hodnôt jednotlivých bitov v bitovom poli.

Ukážme si niekoľko príkladov:

cmp ax, 4       ; porovná register AX s hodnotou 4
cmp dl, ah             ; porovná register DL s registrom AH
cmp [prumerl], ax   ; porovná obsah "premennej" prumer s registrom AX
cmp ax, [prumerl]   ; porovná register AX s obsahom "premennej" prumer
cmp eax, ecx         ; porovná registre EAX a ECX
test ax, 00000100b ; testujeme bit 2 (tretia) bit, ak je nastavený

Inštrukcie nepodmieného skoku JMP

Prvým spôsobom, ako zmeniť sekvenčné vykonávanie inštrukcií v programe, je použitie inštrukcie nepodmienečného skoku. Inštrukcia prinúti procesor spracovávať inštrukcie z iného miesta v pamäti (prepíše hodnoty ukazovateľa na ďalšiu inštrukciu, register IP alebo i CS).

Syntax: JMP typ_skoku operand

Nepodmienený skok známy z vyšších programovacích jazykov pod názvom GOTO v Assembleru predstavuje inštrukcia JMP. Názov vznikol z anglického slova jump (skoč). Inštrukcia vyžaduje jediný priamy operand, ktorým je adresa miesta v pamäti, kam sa má skočiť (ako operand možno použiť aj univerzálny register naplnený adresou skoku, čo by som začiatočníkom neodporúčal). V Assembleri sú rovnako ako vo vyšších programovacích jazykoch adresy skokov reprezentované návestiami (label).
Podľa “vzdialenosti” skoku v programe rozlišujeme tri druhy skokov: short, near a far. (Krátky, blízky a vzdialený).
Maximálna “dĺžka” skoku (maximálny absolútny rozdiel adries, ktorý možeme skokom zmeniť) typu short je obmedzená. V druhom byte inštrukcie je uložená len 8-bitová hodnota sa znamienkom, preto adresa cieľa skoku smie ležať iba v rozmedzí -128 až 127 byte. Skok sa vykoná tak, že sa 8-bitová hodnota znamienkovo rozšíri a pripočíta sa k súčasnej hodnote registra (E)IP.
Inštrukcia skoku typu near už obsahuje vo svojej strojovej reprezentácii novú hodnotu registra (E)IP, dĺžka skoku je obmedzená iba módom procesora. V reálnom režime procesora meníme inštrukciou jmp near register IP, preto sa smieme pohybovať len v rozmedzí jedného segmentu (64 KB), v chránenom režime používame register EIP a tak cieľ skoku môže ležať kdekoľvek v 4 GB adresového priestoru.
Skok far mení aj hodnotu segmentového registra CS, ktorý sa podieľa na výpočte adresy inštrukcie v pamäti. Súčasťou adresy skoku musí byť aj nová hodnota pre register CS.
Ak nie je typ skoku uvedený, predpokladá sa typ near.
Teraz trochu vo výklade “preskočíme” a povieme si, ako vyzerá návestie v Assembleri. Ide v podstate o identifikátor zakončený dvojbodkou, ktorému bola pri preklade pridelená adresa podľa miesta výskytu v programe. Ukážme si časť programu, kde je použité návestie:

mov ax, 4       ; do AX vlož hodnotu 4 
novy_cyklus:   ; návestie novy_cyklus 
mov bx, ax      ; do BX okopírujte AX

Ak chceme vykonať nepodmienený skok na návestie novy_cyklus, napíšeme inštrukciu:

jmp novy_cyklus; skok na novy_cyklus

Po jej prevedení sa program začne vykonávať od návestia novy_cyklus.
Nič nám nebráni najprv vykonať skok a neskôr v programe uviesť návestie. Prekladač pracuje “na viacerých priechodoch”, a preto aj také skoky dopredu vie spracovať. Časť programu používajúca dopredné skoky by mohla vyzerať nasledovne:

jmp start; skok na štart 
ciel:        ; návestie ciel 
...
...         ; ďalšie inštrukcie 
start:    ; návestie štart 
jmp ciel   ; skok na ciel

Vráťme sa ešte krátko k typom skokov, ktoré budeme síce ako začiatočníci využívať veľmi zriedka. Skok typu short použijeme všade tam, kde návestie “leží” do 128 B. Tak ušetríme 1B programového kódu, pretože preložená inštrukcia jmp short zaberá len dva byte (inštrukcia JMP alebo JMP near 3 alebo 5 byte). Možno vás desí predstava, ako odhadnúť vzdialenosť návestí v nepreloženom programe. Nemusíte sa báť, jednoducho to vyskúšajte, prekladač v prípade neúspechu zahlási chybu.

blizke_navestie:              ; návestie blizke_navestie 
....                              ; Nejaké dalšie inštrukcie 
jmp short blizke_navestie  ; urobíme skok na blizke_navestie

Inštrukcie podmienených skokov Jx

Syntax: Jx návestie_ciela_skoku

Ďalším spôsobom, ako zmeniť sekvenčné vykonávanie inštrukcií v programe, je použitie inštrukcie podmieneného skoku.
Inšturkcií podmieneného skoku existuje celá rada. Navzájom sa odlišujú rozhodovaciu podmienkou, ktorá riadi vykonanie skoku. V závislosti od vyhodnotenia podmienky sa vykonávanie programu buď presunie na iné miesto, alebo program pokračuje na nasledujúcu adresu za inštrukciou skoku. Rozhodovaciou podmienkou sú stavy príznakov (niekoľkých alebo len jedného) príznakového registra procesora.
Ukážme si najpoužívanejšie inštrukcie, ktoré skočia na návestie splnené, ak bude podmienka splnená.

jz splnene   ; skočí, ak je príznak ZF (zero flag, príznak nuly) nastavený na 1 
jc splnene  ; skočí, ak je príznak CF (carry flag, príznak prenosu) nastavený na 1 
js splnene  ; skočí, ak je nastavený príznak znamienka SF (signum flag) 
jo splnene  ; skočí, ak je nastavený príznak pretečenia

Všetky podmienky môžeme obrátiť (negovať).

jnz splnene; skočí, ak nie je príznak ZF (zero flag, príznak nuly) nastavený (je nula)

Podobne aj ostatné JNC, JNS a JNO.

Teraz môžeme s našimi znalosťami príslušných inštrukcií Assembleru podmienku IF naprogramovať. Ukážme si jednoduchý príklad, v ktorom budeme chcieť odovzdať riadenie programu (skočiť) na návestie je_tri, ak je hodnota v registri AX rovná trom.
V prvom kroku porovnáme hodnotu v registri AX s číslom 3, čo urobíme inštrukciou CMP.

cmp ax, 3; register AX porovnaj s hodnotou 3

Pri porovnaní na rovnosť použijeme inštrukciu JZ.

jz je_tri; skočí na návestie je_tri, ak bude hodnota AX = 3

Všimnime si, že pri porovnávaní rovnosti čísiel nezáleží na tom, či práve číslo chápeme ako číslo so znamienkom alebo bez.
Inštrukcia JZ vykoná skok na návestie je_tri, ak bude hodnota v registri AX rovná trom, inak bude program pokračovať inštrukciou nasledujúcou po JZ.

V ďalšom príklade budeme chcieť porovnať bez znamienka hodnoty v registri CL a AI .. Do registra BL zapísať jedničku, keď sú si hodnoty rovnaké, dvojku, ak je AL väčší ako CL, a trojku, ak je v registri AL menšie ako v CL

Porovnáme registre AL a CL, výsledok porovnania sa uloží do príznakového registra procesora a použitím rôznych inštrukcií skoku zabezpečíme výber správnej alternatívy a tým aj riešenie problému.

cmp al, cl  ; porovnaj hodnoty uložené v AL a CL 
jz zapis_l   ; skoč na návestie zapis_l, ak AL = CL 
cmp al, cl  ; porovnaj hodnoty uložené v AL a CL 
ja zapis_2 ; skoč na návestie zapis_2, ak AL> CL 
mov bl, 3  ; do BL zapíš 3 
koniec_if:  ; tu je koniec nášho programu 
zapis_1:    ; návestie zapis_1 
mov bl, 1   ; do BL zapíš 1 
jmp koniec_if; skok na koniec nášho programu 
zapis_2:    ; návestie zapis_2 
mov bl, 2   ; do BL zapíš 2 
jmp koniec_if; skok na koniec nášho programu

Museli sme dokonca použiť nepodmienený skok, aby sme vrátili riadenie programu na pôvodné miesto. Riešenie možno ešte výrazne vylepšiť:

mov bl, 1     ; do BL zapíš 1 
cmp al, cl    ; porovnaj AL a CL 
je koniec_if  ; skoč na koniec programu, ak AL = CL 
mov bl, 2    ; do BL zapíš 2 
cmp al, cl   ; porovnaj AL a CL 
ja koniec_if ; skoč na koniec programu, ak AL > CL 
mov bl, 3   ; do BL zapíš 3 
koniec_if:   ; koniec programu
VN:F [1.9.3_1094]
Rating: 9.0/10 (1 vote cast)
Categories: Assembler x86 Tags:

Assembler – inštrukcie DIV a IDIV

September 6th, 2009 admin Žiadne komentáre

Podobne ako inštrukcie MUL má aj inštrukcie DIV tri možné tvary líšiace sa veľkosťou použitých voliteľných operanda.
Syntax:
DIVr/m8; osembitový voliteľný operand v pamäti alebo v registri
DIVr/ml6
DIVr/m32

Voliteľným operand je deliteľ, pevným operand je delenec.
V 8-bitovej variante je voliteľný operand ľubovoľným 8-bitovým registrom alebo miestom v pamäti. Pevný operand sa predpokladá v registri AX, výsledok delenia (podiel) sa uloží do registra AL a zvyšok po delení sa uloží v registri AH.
AX / (r/m8) -> AL, zvyšok v AH
V 16-bitovej variante je voliteľný operand ľubovoľným 16-bitovým registrom alebo miestom v pamäti. Pevný operand sa predpokladá v registrovej dvojici DX: AX, výsledok delenia sa uloží do registra AX, zvyšok po delení sa uloží do registra DX.

DX: AX / (r/ml6) -> AX, zvyšok v DX

V 32-bitovej variante je voliteľný operand ľubovoľným 32-bitovým registrom alebo miestom v pamäti, pevný operand sa predpokladá v registrovej dvojici EDX: EAX, výsledok delenia sa ukladá do registra EAX, zvyšok po delení do registra EDX.

EDX:EAX / (r/m32) -> EAX, zvyšok v EDX

Inštrukciu IDIV použijeme pri delení čísel so znamienkom, operandy inštrukcie sú rovnaké ako u inštrukcie DIV.
Uveďme si opäť niekoľko príkladov.

Príklad: Celočíselne vydeľte číslo 13 dvoma, výsledok uložte do registra BL a zvyšok do registra BH.

mov ax, 13  ; do AX vložíme 13 
mov cl, 2 ; do CL 2
div cl ; vydelíme CL 
mov bx, ax ; výsledok očakávame v BX, stačí prekopírovať
VN:F [1.9.3_1094]
Rating: 7.3/10 (4 votes cast)
Categories: Assembler x86 Tags:

Assembler – inštrukcie ADD,SUB,INC,DEC

September 6th, 2009 admin Žiadne komentáre

Ďalšie často používané inštrukcie sú aritmetické inštrukcie. Procesor 80386 nemá matematický koprocesor, a tak si preberieme len celočíselnú aritmetiku, ktorú 80386 plne podporuje. Všetky aritmetické inštrukcie menia príznaky v priznakovom registri procesora.

Inštrukcie ADD a SUB

Začneme ako v prvej triede s inštrukciou ADD (sčítaj). Inštrukcia ADD má dva operandy, podobne ako inštrukcia MOV.

Syntax: ADD ol, o2

ADD sčíta obidva operandy a výsledok uloží do operanda ol, jeho predchádzajúca hodnota je stratená.

Inštrukcia pre odčítanie je označovaná SUB (z anglického substract) a jej syntax je podobná.

Syntax: SUB ol, o2

Výsledok ol – o2 sa uloží do ol; jeho pôvodná hodnota je opäť stratená. Ukážme si niekoľko príkladov:
Př.: Sčítajme čísla 8 a 6 (uložené v registri AX a CX), výsledok uložme do DX:

mov ax, 8   ; do registra AX vložíme 8 
mov cx, 6   ; do registra CX zase 6 
mov dx, cx  ; CX okopíruje do DX takže DX = 6 
add dx, ax  ; sčítame DX = DX + AX

Pretože sme chceli uchrániť register AX aj CX pred zničením (prepísaním výsledkom), prekopírovali sme hodnotu uloženú v registri CX do DX a spočítali register DX s AX. Inštrukcia ADD výsledok súčtu DX + AX uložila do DX.

Ukážme si ďalšie príklady, ako použiť inštrukcie ADD a SUB.

add eax, 8 ; k registru EAX pripočítame 8
sub ecx, EBP ; registr ECX = ECX - EBP
add byte [cislo], 4 ; k hodnote "premennej" cislo prirátame štyri
  ;(napovedáme rozsah 1 byte 0-256)
sub word [cislo], 4 ; od hodnoty "premennej" cislo odčítame 4
  ;(napovedáme rozsah 2 byte 0-65 535)
add dword [cislo], 4 ;k hodnote "premennej" cislo pripočítame 4
sub byte [cislo], al ;od hodnoty "premennej" cislo odčítame register AL
sub ah, al ; od registra AH odrátame AL a výsledok a uložíme do AH

Inštrukcie INC a DEC

Syntax: 
INC ol  ; ol = ol + 1;
DEC ol ; ol = ol - 1

Ich názvy sú opäť odvodené z angličtiny a znamenajú increment (zvýšiť) a Decre-ment (znížiť).
Inštrukcia zvyšuje (INC) alebo znižuje (DEC) jediný operand o jedničku. Jeho typ je rovnaký ako inštrukcií ADD a SUB. Ak použijeme operand ležiaci v pamäti, musíme prekladaču opäť napovedať požadovaný rozsah operanda. POZOR! Tieto pokyny upravujú príznak prenosu!

Přiklad: pripočítame k registru AI jedničku.

add al, l ; je správne, avšak vďaka inštrukcii INC môžeme požiadavku 
inc al    ; splniť aj takto.

Príklad: Zvýšte hodnotu šestnásťbitové “premennej” cislo o 1.

inc word [cislo]; pripočítali sme jedničku, napovedali sme rozsah 
  ; 0-65535, tj 16 bitov.
VN:F [1.9.3_1094]
Rating: 0.0/10 (0 votes cast)
Categories: Assembler x86 Tags: