Hlavní navigace

Další instrukce zpracovávané v Benderově hlavě

17. 4. 2008
Doba čtení: 12 minut

Sdílet

V osmé části seriálu o architekturách počítačů si řekneme, jakým způsobem je možné načítat konstanty do pracovních registrů mikroprocesoru, popíšeme si instrukce pro přesun registrů z a do operační paměti, instrukce pro skok a návrat z podprogramu a na závěr bitové rotace a aritmetický posun.

Obsah

1. Načtení konstanty do pracovních registrů mikroprocesoru
2. Načtení hodnoty uložené v operační paměti do registru
3. Uložení obsahu registru do operační paměti
4. Skok do podprogramu a návrat z podprogramu
5. Instrukce TEST a příklady jejího využití
6. Bitové rotace
7. Bitové rotace prováděné přes příznak přenosu
8. Aritmetický posun doprava
9. Obsah další části seriálu

1. Načtení konstanty do pracovních registrů mikroprocesoru

V předchozích částech tohoto seriálu jsme si sice popsali mnoho aritmetických i logických instrukcí, které pracují s obsahem pracovních registrů, dosud ovšem nevíme, jak do těchto registrů načíst nějaké konstantní hodnoty. Pro tento účel disponuje náš ukázkový mikroprocesor instrukcí LD, což je mnemotechnická zkratka anglického slova Load. Tato instrukce, kterou disponuje prakticky každý mikroprocesor, má v naší instrukční sadě operační kód 11 (hexadecimálně), za nímž následuje adresní byte, pomocí kterého se rozhoduje, do jakého pracovního registru A či B se má konstanta uložit (tento byte má i další význam, který bude popsán v následující kapitole). Za adresním bytem následuje dvojice bytů (tj. šestnáct bitů) s obsahem, který je načten do zvoleného pracovního registru. Celá instrukce LD má tedy délku čtyř bytů. Obě varianty instrukce, jež se od sebe odlišují pouze cílovým pracovním registrem (tj. adresním bytem), jsou vypsány v následující tabulce.

Strojový kód Zápis v assembleru Význam
11 00 xx yy LD A,#konstanta načtení konstanty xxyy do pracovního registru A
11 01 xx yy LD B,#konstanta načtení konstanty xxyy do pracovního registru B
pc0801

Mikroprocesor provádějící operaci LD A, #konstanta

Následuje jednoduchý příklad použití:

; součet dvou konstant
LD A,#10     ; načtení konstanty 10 do pracovního registru A
LD B,#20     ; načtení konstanty 20 do pracovního registru B
ADD A,B      ; výsledek součtu je uložen do pracovního registru A
             ; a současně jsou ovlivněny oba příznakové registry
             ; Carry flag i Zero flag 

Všimněte si, že se jedná o jednu instrukci náležející do skupiny instrukcí, které ve svém zápisu ve strojovém kódu obsahují i konkrétní hodnotu operandu, se kterým se dále pracuje. Zde se konkrétně jedná o konstantu ukládanou do jednoho z pracovních registrů; u instrukcí skoku se naproti tomu jedná o adresu, na kterou má být skok proveden. Tyto hodnoty do strojového kódu vkládá přímo překladač a v případě, že se nepoužijí různé „špinavé“ triky typu automodifikujícího se kódu (ten si možná také někdy popíšeme), jsou tyto konstanty uloženy přímo ve spustitelných souborech, které ve své nejjednodušší podobě představují strojový kód přeloženého programu.

Dále stojí za pozornost vlastní způsob uložení konstanty v adresní části instrukce. Při rozložení šestnáctibitového čísla na dva byty, které představují nejmenší adresovatelnou jednotku operační paměti, je totiž možné použít dva hlavní způsoby – little endian (první byte uložený v operační paměti či registru obsahuje v případě šestnáctibitového mikroprocesoru bity 0–7) a big endian (první byte obsahuje bity 8–15). Různé mikroprocesory, resp. různí výrobci mikroprocesorů, používají i různý způsob uložení, proto se někdy můžeme setkat s označením Intel order (little endian) a Motorola (Sun) order (big endian). Každý z obou způsobů má své přednosti i zápory – při použití little endian se snáze provádí konverze mezi různými datovými typy, big endian se zase snáze čte z výpisů obsahu operační paměti. Ovšem většinou (jak je to běžné v podobných případech) je zcela jedno, který způsob daný mikroprocesor používá, jen je zapotřebí pamatovat na to, že u některých instrukcí je znalost konkrétního způsobu uložení bytů zcela nezbytná (práce s binárními soubory, síťové protokoly, ovládání periferních zařízení atd.).

pc0802

Mikroprocesor provádějící operaci LD B, #konstanta

2. Načtení hodnoty uložené v operační paměti do registru

Instrukce pro načtení hodnoty uložené na libovolné adrese v operační paměti do pracovního registru je už na první pohled velmi podobná předchozí instrukci. Jediný, zato však podstatný rozdíl spočívá v tom, že v samotné instrukci není uložena konstanta, která se má do zvoleného pracovního registru načíst, ale šestnáctibitová adresa, která určuje paměťové místo, na kterém požadovaná hodnota leží. I samotný zápis instrukce v assembleru (jazyku symbolických adres) je poněkud odlišný, aby na první pohled bylo patrné, o jakou instrukci se jedná. Zatímco před konstantou byl uveden znak křížku „#“, je adresa zapsána do hranatých závorek (ve své podstatě se jedná o mix syntaxí mikroprocesorů Motorola a Intel). Operační kód instrukce LD registr, [adresa] je shodný s operačním kódem instrukce LD registr, #konstanta, ovšem liší se adresní byte (konkrétně: pátý bit je nastaven na jedničku), což je ostatně patrné při porovnání tabulky uvedené v první kapitole s tabulkou zobrazenou níže.

Strojový kód Zápis v assembleru Význam
11 10 xx yy LD A,[adresa] načtení hodnoty uložené v operační paměti do pracovního registru A
11 11 xx yy LD B,[adresa] načtení hodnoty uložené v operační paměti do pracovního registru B

I samotné provedení instrukce je poněkud odlišné. Zatímco při načítání konstanty se z operační paměti přečetlo šestnáctibitové slovo uložené za adresním bytem a ihned poté se přes interní sběrnici zapsalo do zvoleného pracovního registru, musí se při načítání hodnoty z určené adresy pomocí instrukce LD registr, [adresa] operační paměť obsloužit (číst) dvakrát. Při prvním čtení operační paměti se načte samotná instrukce s adresním bytem a šestnáctibitovou adresou. Posléze je na adresovou sběrnici poslána přečtená adresa, pomocí které se v operační paměti vybere a následně vrátí obsah zvolené paměťové buňky (a buňky s adresou o jedničku větší, protože pracujeme se šestnáctibitovými slovy a paměť je adresována po bytech) a teprve tento obsah je přes interní sběrnici mikroprocesoru zapsán do zvoleného pracovního registru. Rozdíl mezi oběma způsoby načtení hodnoty do pracovního registru je ilustrován na následujícím obrázku:

pc0803

Instrukce LD A,#1234

pc0804

Instrukce LD A,[1234]

3. Uložení obsahu registru do operační paměti

Kromě načítání hodnot do pracovních registrů je samozřejmě nutné provádět i opačnou operaci, tj. uložení hodnoty pracovního registru na zvolené místo v operační paměti. Pro tento účel obsahuje náš ukázkový mikroprocesor instrukci nazvanou ST, což je mnemotechnická zkratka anglického slova Store. Strojový kód této instrukce je dlouhý čtyři byty. V prvním byte je operační kód (opkód) s hodnotou 0×12 za nímž následuje adresový byte, s jehož pomocí je zvolen jeden z pracovních registrů. Poslední dva byty obsahují šestnáctibitovou adresu, na kterou se hodnota pracovního registru uloží (ve skutečnosti se hodnota rozloží do dvou paměťových buněk, protože pracovní registr má šířku šestnáct bitů a buňka paměti je osmibitová). Vzhledem k tomu, že tento mikroprocesor může paměť obsluhovat pouze pomocí instrukcí LD a ST, náleží do kategorie mikroprocesorů nazývaných někdy termínem Load-Store machines. Prakticky všechny RISC procesory náleží do této kategorie, bližší informace si však uvedeme až příště.

Strojový kód Zápis v assembleru Význam
12 00 xx yy ST A,[adresa] uložení hodnoty z pracovního registru A do operační paměti na zadanou adresu
12 01 xx yy ST B,[adresa] uložení hodnoty z pracovního registru B do operační paměti na zadanou adresu
pc0805

Instrukce ST A,[1234]

4. Skok do podprogramu a návrat z podprogramu

Pro implementaci podprogramů, které je možné volat i rekurzivně, náš mikroprocesor obsahuje instrukce určené pro skok do podprogramu a instrukci pro návrat z podprogramu. Instrukce pro skok do podprogramu se jmenuje CALL, o návrat z podprogramu se stará instrukce RET (Return). Skok do podprogramu je realizován prakticky stejným způsobem jako běžný skok, tj. adresa zapsaná za operačním kódem instrukce je vložena do registru PC, čímž dojde k přemístění řízení na uvedenou adresu. Ovšem původní hodnota registru PC je uložena na zásobník, takže si mikroprocesor vlastně zapamatuje, na které adrese se před skokem do podprogramu nacházel. Instrukce RET vyjme hodnotu uloženou na vrcholu zásobníku a přenese ji do pracovního registru PC (ve skutečnosti se do tohoto registru přenese hodnota o tři větší, protože samotná instrukce CALL má délku přesně tři byty). To znamená, že po provedení instrukce RET se řízení programu vrátí na adresu, která leží těsně za původní instrukcí CALL.

Strojový kód Zápis v assembleru Význam
17 xx yy CALL adr skok na adresu xxyy s uložením stavu PC na zásobník
18 RET návrat na adresu, jež je uložena na vrcholu zásobníku (obnovení PC)

Instrukci RET je možné použít ve funkci „aritmetického GOTO“. Jedná se o příkaz známý například z některých interpretů programovacího jazyka Basic, ve kterých bylo možné provádět skok na řádek, jehož číslo bylo vypočtené pomocí aritmetického výrazu. V assembleru se tato technika používá velmi často, například při implementaci rozhodovacích tabulek (decision tables), popřípadě jako náhrada za strukturovaný příkaz typu switch-case. Vzhledem k tomu, že náš mikroprocesor neobsahuje instrukci nepřímého skoku typu JMP A či JMP B, musí se „aritmetické GOTO“ vytvořit nepřímo. Využijeme přitom toho, že instrukce RET očekává na vrcholu zásobníku adresu, která se načte do registru PC, protože je možné pomocí dále popsaných instrukcí PUSH A či PUSH B do zásobníku uložit obsah jednoho z pracovních registrů a poté zavolat instrukci RET, která požadovaný nepřímý skok provede. Tento způsob využití (či spíše zneužití) zásobníku je v některých případech použit například i při pokusu o napadení nativních aplikací. Použitím zásobníku se budeme podrobněji zabývat v následující části tohoto seriálu.

5. Instrukce TEST a příklady jejího využití

Instrukce TEST slouží k provedení logické operace konjunkce (and) bit po bitu s požadovanou dvojicí pracovních registrů, tj. jedná se o stejnou funkci, jakou provádí již minule popsaná instrukce AND. Jediný, zato však podstatný rozdíl spočívá v tom, že zatímco instrukce AND uložila výsledek operace do pracovního registru, který byl jejím prvním operandem, tak instrukce TEST výsledek operace „zapomene“, tj. obsah žádného pracovního registru se nezmění. Nastaví se však příznakový bit Zero flag v závislosti na tom, zda je výsledek konjunkce nulový či nenulový. Tato instrukce se používá především při implementaci různých podmínek či programových smyček, podobně jako minule popsaná instrukce CMP. Všechny čtyři varianty této instrukce jsou vypsány v níže uvedené tabulce, ze které je patrné, že je možné provádět test registru se sebou samým. Mezi operacemi TEST A,B a TEST B,A není žádný podstatný rozdíl, protože v tomto případě nezáleží na pořadí operandů (na rozdíl od instrukce CMP).

pc0806

Mikroprocesor provádějící operaci TEST A,A

Strojový kód Zápis v assembleru Význam
10 00 TEST A,A A & A
10 01 TEST A,B A & B
10 10 TEST B,A B & A
10 11 TEST B,B B & B

Typické použití této instrukce spočívá v implementaci podmínky typu if (a and b) či pro test na nulovost některého z registrů, protože příznakový bit Zero flag je u instrukce TEST A,A resp. TEST B,B nastaven na jedničku pouze v případě, že je obsah pracovního registru nulový. Připomeňme si, jak instrukce AND a tím i instrukce TEST zpracuje různé hodnoty (v případě instrukce TEST není výsledek nikam uložen, „pouze“ se nastaví příznak Zero flag):

hex binárně Zero flag
operand 1 0000 0000000000000000
operand 2 0000 0000000000000000
výsledek 0000 0000000000000000 1
operand 1 2A2A 0010101000101010
operand 2 00FF 0000000011111111
výsledek 002A 0000000000101010 0
operand 1 0FF0 0000111111110000
operand 2 FF00 1111111100000000
výsledek 0F00 0000111100000000 0
operand 1 00FF 0000000011111111
operand 2 FF00 1111111100000000
výsledek 0000 0000000000000000 1
pc0807

Mikroprocesor provádějící operaci TEST A,B

6. Bitové rotace

V assembleru se poměrně často používají instrukce provádějící takzvané bitové rotace. O co se vlastně jedná? Jde o posun všech šestnácti bitů v některém z pracovních registrů, ovšem tak, že se jedná o uzavřenou smyčku, tj. obsah bitu, který by se provedením posunu ztratil (byl by vysunut), je naopak zkopírován na vstup. Kromě toho se tento bit zkopíruje do příznakového bitu Carry flag, což není samoúčelné, protože se této skutečnosti dá využít pro provedení skoku v závislosti na hodnotě jednoho konkrétního bitu, implementaci generátoru pseudonáhodných čísel nebo při programování aritmetických operací násobení a dělení, pro které nemá náš mikroprocesor specializované instrukce ani obvody. Pro takto chápanou bitovou rotaci existují dvě instrukce, první pro rotaci obsahu pracovního registru doleva a druhá pro rotaci doprava. Každá instrukce způsobí rotaci pouze o jeden bit, v případě potřeby provedení většího množství rotací je nutné instrukce zopakovat (většina moderních mikroprocesorů používá takzvaný barell shifter, což je obvod umožňující bitový posun či rotaci i o větší počet bitů). Všechny čtyři instrukce (levá a pravá rotace) aplikované na jeden z pracovních registrů, jsou vypsány v tabulce:

Strojový kód Zápis v assembleru Význam
0a 00 RL A bitová rotace registru A doleva
0a 01 RL B bitová rotace registru B doleva
0c 00 RR A bitová rotace registru A doprava
0c 01 RR B bitová rotace registru B doprava

Jak samotná instrukce rotace ovlivní obsah pracovního registru i příznakový bit Carry flag, je ukázáno na příkladu níže:

registr A (binárně) instrukce po provedení instrukce Carry flag
0000000000000001 RL A 0000000000000010 0
1000000000000000 RL A 0000000000000001 1
0000000000000001 RR A 1000000000000000 1
1000000000000000 RR A 0100000000000000 0
pc0808

Rotace doleva i doprava

7. Bitové rotace prováděné přes příznak přenosu

Kromě běžných bitových rotací má mnoho mikroprocesorů implementovány i rotace prováděné přes příznak přenosu (Carry flag). V podstatě se jedná o rozšíření bitové šířky rotovaného registru o jeden bit, který je představovaný právě příznakem přenosu, tj. neprovádí se rotace šestnácti bitů, ale bitů sedmnácti. Použití těchto typů rotací spočívá například v implementaci víceslovní aritmetiky (rotuje se přes více šestnáctibitových slov), popř. při požadavku na vložení hodnoty příznaku přenosu do určeného bitu pracovního registru (booleovské operace). V případě, že je příznak přenosu vhodnou instrukcí nastaven na nulu, stávají se z těchto rotací vlastně bitové posuvy (bit shifts); z tohoto důvodu je není nutné do instrukční sady přidávat. Nákres rotace doleva i doprava přes příznak přenosu, je naznačen na obrázku:

pc0809

Rotace doleva i doprava přes příznak přenosu

Strojový kód Zápis v assembleru Význam
0b 00 RLC A bitová rotace registru A doleva přes příznak přenosu
0b 01 RLC B bitová rotace registru B doleva přes příznak přenosu
0d 00 RRC A bitová rotace registru A doprava přes příznak přenosu
0d 01 RRC B bitová rotace registru B doprava přes příznak přenosu

8. Aritmetický posun doprava

Ke čtyřem typům bitových rotací je v instrukční sadě našeho učebního mikroprocesoru přidán i takzvaný aritmetický posun doprava (ASR=Arithmetic Shift Right). Jedná se vlastně o obdobu bitového posunu doprava, ovšem s tím rozdílem, že obsah nejvyššího bitu zůstává za všech okolností zachován (a samozřejmě je ještě zkopírován do druhého nejvyššího bitu). Pokud si uvědomíme, jakým způsobem jsou reprezentovány hodnoty v systému dvojkového doplňku (viz předchozí části tohoto seriálu), je zřejmé, že tato instrukce ve většině případů (99,99% u šestnáctibitových čísel) provádí dělení dvěma, a to i pro záporné hodnoty. Pro aritmetický posun doleva žádná specializovaná instrukce není dostupná, protože ji lze nahradit běžným posunem (platí i pro záporná čísla reprezentovaná v systému dvojkového doplňku).

pc0810

Aritmetický posun doprava

root_podpora

Strojový kód Zápis v assembleru Význam
0e 00 0× ASR A aritmetický posuv registru A doprava o x bitů
0e 01 0× ASR B aritmetický posuv registru B doprava o x bitů

9. Obsah další části seriálu

V následující části seriálu o činnosti počítače konečně dokončíme popis celého instrukčního souboru našeho cvičného mikroprocesoru popisem zbývajících instrukcí IRET, NOP a HALT i instrukcí pro práci se zásobníkem. Ukážeme si také, jakým způsobem mikroprocesor reaguje na vnější či vnitřní přerušení a z jakého důvodu se vlastně přerušení u mikroprocesorů (a počítačů obecně) používá. Také si řekneme, jaké existují architektury mikroprocesorů, způsob členění mikroprocesorů do kategorií a přednosti i zápory jednotlivých architektur.

Byl pro vás článek přínosný?

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.