Dnes vyšlo 1 článků

Složitější programové konstrukce – programování v Assembleru – 3. díl

Složitější programové konstrukce  – programování v Assembleru – 3. díl

Úvodem tohoto seriálu bude řešení úloh z konce minulé části seriálu. Bude sloužit jako takový test, zda jste skutečně pochytili vše, co jste měli a má cenu tedy pokračovat dále. A nebo ještě jednou prostudovat předchozí části seriálu.

Jaký je rozdíl mezi Assemblerem a jazykem C? Je Assembler vhodný pro tvorbu rozsáhlých programů?

Plnohodnotnou odpověď na tuto otázku nám poskytne odstavec z první části seriálu Co je Assembler zač?.

 Převeďte do dvojkové soustavy čísla 18, 12 a -8 užitím doplňkového kódu.

18 – (00010010)2

12– (00001100)2  - postupně dělíme dvojkou a zapisujeme zbytek v podobě jedničky a nuly. Postup je popsaný v tomto materiálu na straně 4: http://uloz.to/xisVCrZ/ciselne-soustavy-pdf

Při tvorbě záporného čísla metodou doplňkového kódu zaměníme veškeré bity absolutní hodnoty daného čísla za opačné a následně přičteme jedničku :

-8 = číslo 8 je binárně zapsáno (00001000)2, po záměně bitů za opačné nám vznikne (11110111)2 a následným přičtením čísla 1 dostaneme (11111000)2, tedy požadované -8 zapsáno v doplňkovém kódu.

Které z těchto instrukcí mají pouze jeden operand?

Pouze jeden operand mají tyto 3 instrukce: DIV, IDIV, DEC.

Je správný tento zápis: SUB EAX, 4?

Tato otázka byla myšlena jako „chyták“, který prověří, zda jste správně pochopili stavbu instrukcí. Operandem nemůže být číslo pouze u jednooperandových instrukcí, jako např. DIV. Pokud jste na tuto otázku neodpověděli správně, doporučuji ještě jednou si pečlivě projít základní aritmetické instrukce zejména s důrazem na jejich zápis a  možné tvary operandů.

Jakým příkazem (příkazy) vypíšeme hodnotu EDX na obrazovku?

Abychom mohli vypsat hodnotu EDX na obrazovku, musíme její obsah přesunout nejdříve do registru EAX a následně provedeme příkaz „call WriteInt32“.

Napište program, který načte dvě čísla (uživatel je zadá pomocí klávesnice), první o jedničku zmenší, ke druhému přičte osmičku. Provede dělení se zbytkem a zbytek vypíše na obrazovku. Při počítání užívejte 32-bitové registry.

Program je trochu podobný tomu, který jsme si společně zkoušeli ke konci minulé části seriálu. Princip tedy bude velmi podobný a pokud jste dobře porozuměli příkladu demonstrovaném v minulém díle, neměli byste mít s řešením žádný problém.

Okomentovaný zdrojový kód:

Call ReadInt32                       ; načtení prvního čísla z klávesnice, číslo se nahraje do EAX

MOV EBX, EAX                       ; zálohování prvního čísla, použít můžeme i jiný registr než EBX

Call ReadInt32                       ; načtení druhého čísla z klávesnice, číslos bude nacházet v EAX

DEC EBX                     ; první číslo zmenšíme o 1

ADD EAX, 8                 ; ke druhému číslu přičteme konstantu 8

IDIV EBX                     ; EAX/EBX, zbytek se ukládá do EDX

MOV EAX, EDX           ; přesunutí obsahu EDX do EAX, abychom mohli zbytek vypsat

Call WriteInt32           ; výpis zbytku po dělení na obrazovku

Základní instrukce a principy tohoto jazyka už tedy známe. V tomto díle si předvedeme ještě další instrukce, které slouží pro operaci s binárními čísly. V další části si vysvětlíme, jak provést větvení programu na základě podmínek a jak se v Assembleru tvoří cykly, které dobře znají čtenáři, kteří už v jakémkoliv jazyce zkoušeli programovat. Bez větvení a cyklů ani žádný plnohodnotný program napsat nikdy nemůžeme.

Bitové operace

Jdeme si tedy trochu pohrát s nulami a jedničkami. Začneme vysvětlením, jak je v Assembleru možno užít operace AND, OR, XOR a negaci. Princip těchto operací byl vysvětlen v materiálu Číselné soustavy.

Zapsání těchto operací je velmi jednoduché, stačí jen napsat název operace a doplnit příslušné operandy:

MOV AL, 10010100b ; naplníme registr AL binárním číslem, b za číslem značí překladači, že se    ;jedná o binární číslo

AND můžeme využít v praxi například pro vyprázdnění části registru. Druhý operand připravíme tak, že na pozice, na kterých chceme, aby zůstaly ve výsledku původní hodnoty registru, umístíme jedničky. Pro vyprázdnění bitové pozice použijeme 0, neboť pokud je alespoň jeden člen u operace AND 0, je výsledkem operace vždy 0. Operace 0R funguje obráceně. (viz materiál). Pro lepší pochopení si to předvedeme prakticky. Pomocí AND vyprázdníme horní část (první čtyři bity) registru AL:

AND AL, 00001111b ; operand má na prvních čtyřech pozicích nulu, vynuluje tedy nejvyšší ;čtyři bity AL, druhá část zůstane zachována, hodnota AL tedy bude 00000100

Operace OR se přímo vybízí pro naplnění části registru jedničkami, předvedeme si tedy naplnění vrchní části AL samými jedničkami (pracujeme s původní hodnotou AL):

OR AL, 11110000b ; první polovina AL se změní v samé jedničky, druhá polovina zůstane ;zachovalá, čili AL = 11110100

Operaci XOR již není nutné předvádět, neboť její princip je obdobný jako v případě OR

NOT vs NEG

Operace NOT pouze zamění jedničky za nuly a naopak, NEG navíc provede i přičtení jedničky, čili operace NEG provádí to správné matematické negování  kladného čísla na záporné.

Počítáme s tím, že registr AL máme zase naplněn hodnotou 10010100b.

NOT AL ; Výsledkem bude 01101011b, čili zaměněné bity

NEG AL ; Výsledná hodnota bude 01101100b, ještě přičteme jedničku k zaměněným bitům

Pozn. U logických instrukcí může být druhým operandem i registr!

Ti, kteří pozorně četli první díl seriálu, už si určitě uvědomili, že NEG je vlastně dvojkový doplněk.

Posuvy a rotace

Tyto instrukce slouží pro manipulaci s jednotlivými bity uvnitř registru. Nejlépe se dají pochopit přímo z příkladů, přistoupíme tedy ihned na ně. U každého příkazu je na prvním řádku zobrazena původní hodnota AL a druhý řádek znázorňuje stav registru po provedení instrukce. Pro lepší přehlednost je každý bit v samostatné buňce. Tučně jsou zvýrazněny původní bity

SHR AL,            ; posun vpravo o 3 bity, veškeré bity se posunou o 3 místa vpravo, na ;prázdných místech zůstávají nuly.

1 0 0 0 1 0 1 1
0 0 0 1 0 0 0 1

SHL AL, 3         ; posun vlevo o 3 bity, veškeré bity se posunou o 3 místa vlevo, na prázných ;místech zůstávají nuly.

1 0 0 0 1 0 1 1
0 1 0 1 1 0 0 0

ROL AL, 3        ; ROTACE vlevo o 3 bity, rozdíl mezi posuvem a rotací je takový, že u rotace ;nahradí prázdná místa rotované registry, nebudou tam tedy jen samé nuly, jako u posunů. ;Modře je vyznačená část, která rotovala.

1 0 0 0 1 0 1 1
0 1 0 1 1 1 0 0

ROR AL, 3 ; ROTACE vpravo o 3 bity

1 0 0 0 1 0 1 1
0 1 1 1 0 0 0 1

Některé z těchto instrukcí si nyní vyzkoušíme prakticky na příkladě. Budeme vytvářet program, který zamění horní polovinu registru AL s dolní polovinou registru BL  – 4 bity, druhou z částí u obou registrů vymaže. Pro správné pochopení uvedu příklad:

AL = 10001011b         po úpravě programem AL = 00100000b

BL = 10110010b         po úpravě programem BL = 00001000b

Je pochopitelně mnoho způsobů, jak úlohu vyřešit. Než tedy začnete pročítat jedno z možných řešení programu, zkuste sami zapřemýšlet a pokud možno vyvinout co nejefektivnější řešení. Na tento program je zapotřebí pouze logické myšlení a znalost instrukcí probíraných v tomto dílu. Pokud si ale nebudete vědět rady, tak přikládám okomentovaný zdrojový kód.

Call ReadInt8 ; Pozor! Čísla nelze zadávat z klávesnice v binární podobě, zadáme dekadicky.

MOV BL, AL ; obsah AL vložíme do BL

Call ReadInt8 ; načteme číslo do registru AL

SHL BL, 4; posun bitů o 4 vlevo

SHR AL, 4; posun bitů u AL O 4 vpravo

;protože nemusíme zachovat druhou část registrů, stačí nyní už pouze zaměnit obsah ;registrů, k tomu slouží instrukce XCHG (exchange):

xchg AL, BL; a je hotovo, můžeme si vyzkoušet správnost užitím funkce

;WriteBin8 zkusit vypsat obsahy registrů před a po operacích

Vytvořit tento program nebylo těžké. Trošku víc zabrat mozkovým závitům ale dá tvorba programu, který místo vyprázdnění zbývajících částí registru je musí zachovat v původním stavu, čili provede toto:

AL = 10001011b         po úpravě programem AL = 00101011b

BL = 10110010b         po úpravě programem BL = 10111000b

Zapojte pořádně mozkové závity a zkuste tento úkol do příštího dílu vyřešit. V příštím díle si detailně rozebereme tento příklad a demonstrujeme jednu z možných variant řešení.

Větvení programu a cykly

Ve většině jazyků pro tvorbu podmínek využíváme různé varianty konstrukce IF. Assembler tuto problematiku řeší pomocí porovnávání a skoků. My si uvedeme základní druhy skoků:

Instrukce porovnávání hodnot:

CMP o1, o2     ; druhým operandem může být číslo nebo registr, porovnává hodnoty operandů

Druhou porovnávací instrukcí je insrtrukce TEST:

TEST o1, 00000010b   ; zadaný příkaz zjišťuje, zda-li je u operandu o1 druhý bit zprava zapnutý ;(má hodnotu 1), instrukce TEST je logický AND, ;ovšem nepřepíše nijak hodnotu operandu o1, ;namísto binárního čísla může být i registr. Instrukce TEST tedy slouží pro porovnávání ;jednotlivých  bitů binárního čísla

JMP návěští – nepodmíněný skok, přeskočíme přímo na uvedené návěští (návěští je jakási část programu, k jejíž vykonávání se může pomocí skoku ve kterékoliv části programu přejít)

JE (o1=o2) návěští – na návěstí přeskočíme, pokud se hodnoty rovnají nebo má o1 hodnotu 0.

JL (o1<o2)  návěští – přeskočíme, pokud je první operand menší

JG (o1>o2) návěští – přeskočíme, pokud je první operand větší

JC (je aktivní příznak přenosu) návěští – přeskočíme, pokud je CF = 1

Skoky mají i varianty not – např. JNE (jump not equal) = skok, pokud hodnoty nejsou stejné

Ještě tu zmíním i instrukci LOOP – instrukci smyček. Provede skok na návěstí n-krát, kdy n je hodnota registru ECX. Zde je krátký prográmek obsahující instrukci LOOP:

MOV ECX, 4    ; do ECX nahrajeme číslo 4

smycka:

INC EBX           ; EBX = EBX+1

LOOPsmycka ; provede se 4-krát návěstí smycka – instrukce INC EBX, neboť ECX = 4

Komentáře

Nahoru