Jazyk symbolických adres
Jazyk assembleru je programovací jazyk, který lze použít k přímému zadávání příkazů počítači. Jazyk assembleru je téměř stejný jako strojový kód, kterému počítač rozumí, jen místo čísel používá slova. Počítač ve skutečnosti nemůže přímo rozumět programu v assembleru. Může však snadno změnit program na strojový kód tím, že slova programu nahradí čísly, která označují. Program, který to dělá, se nazývá assembler.
Programy napsané v jazyce assembleru se obvykle skládají z instrukcí, což jsou malé úlohy, které počítač provádí při spuštění programu. Nazývají se instrukce, protože programátor jimi dává počítači pokyny, co má dělat. Část počítače, která se instrukcemi řídí, je procesor.
Jazyk assembleru počítače je nízkoúrovňový jazyk, což znamená, že se dá použít pouze k jednoduchým úlohám, kterým počítač přímo rozumí. Aby bylo možné provádět složitější úlohy, je třeba počítači sdělit každou z jednoduchých úloh, které jsou součástí složité úlohy. Počítač například nerozumí tomu, jak vytisknout větu na obrazovku. Místo toho mu musí program napsaný v assembleru říci, jak má provést všechny malé kroky, které se na vytištění věty podílejí.
Takový assembler by se skládal z mnoha a mnoha instrukcí, které dohromady dělají něco, co se člověku zdá velmi jednoduché a základní. Proto je pro člověka obtížné program v assembleru číst. Naproti tomu vysokoúrovňový programovací jazyk může obsahovat jedinou instrukci, například PRINT "Hello, world!", která počítači řekne, aby za vás provedl všechny drobné úkony.
Vývoj jazyka Assembly
Když počítačoví vědci poprvé sestrojili programovatelné stroje, naprogramovali je přímo ve strojovém kódu, což je řada čísel, která počítači přikazují, co má dělat. Psaní strojového jazyka bylo velmi obtížné a trvalo dlouho, takže nakonec vznikl jazyk assembler. Jazyk assembleru je pro člověka snáze čitelný a lze jej psát rychleji, ale stále je pro člověka mnohem obtížnější než programovací jazyk vysoké úrovně, který se snaží napodobit lidskou řeč.
Programování ve strojovém kódu
Aby mohl programátor programovat ve strojovém kódu, musí vědět, jak vypadají jednotlivé instrukce v binárním (nebo hexadecimálním) kódu. Ačkoli pro počítač je snadné rychle zjistit, co strojový kód znamená, pro programátora je to obtížné. Každá instrukce může mít několik podob, z nichž všechny pro člověka vypadají jen jako hromada čísel. Jakékoli chyby, které se někdo při psaní strojového kódu dopustí, si všimne až ve chvíli, kdy počítač provede špatnou věc. Přijít na chybu je těžké, protože většina lidí nedokáže při pohledu na strojový kód určit, co znamená. Příklad toho, jak vypadá strojový kód:
05 2A 00
Tento hexadecimální strojový kód říká procesoru počítače x86, aby do akumulátoru přičetl 42. Pro člověka je velmi obtížné jej přečíst a pochopit, i když zná strojový kód.
Použití jazyka Assembly místo toho
V jazyce assembleru lze každou instrukci zapsat jako krátké slovo, tzv. mnemotechnickou pomůcku, za kterou následují další věci, jako jsou čísla nebo jiná krátká slova. Mnemotechnické znaky se používají proto, aby si programátor nemusel pamatovat přesná čísla ve strojovém kódu potřebná k tomu, aby počítači řekl, aby něco udělal. Příkladem mnemotechnických výrazů v jazyce assembleru je add, který sčítá data, a mov, který přesouvá data z jednoho místa na druhé. Protože "mnemotechnika" je neobvyklé slovo, používá se někdy místo něj, často nesprávně, výraz typ instrukce nebo jen instrukce. Slova a čísla za prvním slovem poskytují další informace o tom, co se má provést. Například věci následující za sčítáním mohou říkat, jaké dvě věci se mají sečíst, a věci následující za mov říkají, co se má přesunout a kam se to má umístit.
Například strojový kód z předchozí části (05 2A 00) lze v assembleru zapsat jako:
Jazyk assembleru také umožňuje programátorům jednodušší zápis skutečných dat, která program používá. Většina jazyků assembleru má podporu pro snadné vytváření čísel a textu. Ve strojovém kódu by se každý jiný typ čísla, například kladné, záporné nebo desetinné, musel ručně převádět do dvojkové soustavy a text by se musel definovat po jednotlivých písmenech jako čísla.
Jazyk assembleru poskytuje tzv. abstrakci strojového kódu. Při použití assembleru nemusí programátoři znát podrobnosti o tom, co čísla pro počítač znamenají, to místo nich zjistí assembler. Jazyk assembleru vlastně stále umožňuje programátorovi využívat všechny funkce procesoru, které by mohl využívat se strojovým kódem. V tomto smyslu má jazyk assembleru velmi dobrou a vzácnou vlastnost: má stejnou schopnost vyjadřovat věci jako věc, od které abstrahuje (strojový kód), a přitom je mnohem jednodušší na používání. Z tohoto důvodu se strojový kód jako programovací jazyk téměř nepoužívá.
Demontáž a ladění
Když jsou programy hotové, jsou již převedeny do strojového kódu, takže je procesor může skutečně spustit. Někdy se však stane, že pokud je v programu chyba (omyl), chtějí programátoři vědět, co která část strojového kódu dělá. Disassemblery jsou programy, které programátorům pomáhají to udělat tím, že strojový kód programu převedou zpět do jazyka assembleru, který je mnohem srozumitelnější. Disassemblery, které mění strojový kód na jazyk assembleru, dělají opak assemblerů, které mění jazyk assembleru na strojový kód.
Organizace počítače
K pochopení fungování programu v jazyce assembleru je zapotřebí porozumět tomu, jak jsou počítače uspořádány, jak zdánlivě fungují na velmi nízké úrovni. Na nejjednodušší úrovni mají počítače tři hlavní části:
- hlavní paměť nebo RAM, která uchovává data a instrukce,
- procesor, který zpracovává data prováděním instrukcí, a
- vstup a výstup (někdy zkráceně I/O), které umožňují počítači komunikovat s okolním světem a ukládat data mimo hlavní paměť, aby je mohl později získat zpět.
Hlavní paměť
Ve většině počítačů je paměť rozdělena na bajty. Každý bajt obsahuje 8 bitů. Každý bajt v paměti má také adresu, což je číslo, které udává, kde se daný bajt v paměti nachází. První bajt v paměti má adresu 0, další má adresu 1 atd. Rozdělení paměti na bajty umožňuje její adresování, protože každý bajt dostane jedinečnou adresu. Adresy bajtových pamětí nelze použít k odkazu na jednotlivý bit bajtu. Bajt je nejmenší část paměti, kterou lze adresovat.
Přestože adresa odkazuje na konkrétní bajt v paměti, procesory umožňují používat několik bajtů paměti za sebou. Nejčastějším využitím této funkce je použití 2 nebo 4 bajtů v řadě k reprezentaci čísla, obvykle celého. K reprezentaci celých čísel se někdy používají také jednotlivé bajty, ale protože mají délku pouze 8 bitů, mohou obsahovat pouze 2 8nebo 256 různých možných hodnot. Použití 2 nebo 4 bajtů v řadě zvyšuje počet různých možných hodnot na 2 16, 65536, resp. 2 32, 4294967296.
Když program používá bajt nebo několik bajtů za sebou k reprezentaci něčeho, jako je písmeno, číslo nebo cokoli jiného, nazývají se tyto bajty objektem, protože jsou všechny součástí jedné věci. Přestože jsou všechny objekty uloženy v identických bajtech paměti, zachází se s nimi, jako by měly "typ", který říká, jak mají být bajty chápány: buď jako celé číslo, nebo znak, nebo nějaký jiný typ (například neceločíselná hodnota). Strojový kód lze také považovat za typ, který je interpretován jako instrukce. Pojem typ je velmi, velmi důležitý, protože definuje, jaké věci lze a nelze s objektem provádět a jak interpretovat bajty objektu. Například do objektu s kladným číslem nelze uložit záporné číslo a do objektu s celým číslem nelze uložit zlomek.
Adresa, která ukazuje na vícebajtový objekt (je jeho adresou), je adresou prvního bajtu tohoto objektu - bajtu, který má nejnižší adresu. Na okraj je třeba poznamenat, že podle adresy objektu nelze určit jeho typ - a dokonce ani jeho velikost. Ve skutečnosti ani nemůžete zjistit, jaký typ objektu je, když se na něj podíváte. Program v assembleru musí sledovat, na kterých paměťových adresách jsou umístěny které objekty a jak jsou tyto objekty velké. Program, který to dělá, je typově bezpečný, protože s objekty provádí pouze ty činnosti, které jsou bezpečné pro jejich typ. Program, který to nedělá, pravděpodobně nebude fungovat správně. Všimněte si, že většina programů ve skutečnosti explicitně neukládá, jaký je typ objektu, pouze k objektům přistupuje konzistentně - se stejným objektem se vždy zachází jako se stejným typem.
Procesor
Procesor spouští (vykonává) instrukce, které jsou uloženy v hlavní paměti jako strojový kód. Kromě možnosti přístupu do paměti pro ukládání dat má většina procesorů několik malých, rychlých prostorů s pevnou velikostí pro uložení objektů, se kterými se právě pracuje. Tyto prostory se nazývají registry. Procesory obvykle provádějí tři typy instrukcí, i když některé instrukce mohou být kombinací těchto typů. Níže je uvedeno několik příkladů jednotlivých typů v assembleru x86.
Instrukce pro čtení nebo zápis do paměti
Následující instrukce jazyka assembleru x86 načte (načte) dvoubajtový objekt z bajtu na adrese 4096 (0x1000 v šestnáctkové soustavě) do 16bitového registru nazvaného 'ax':
V tomto jazyce assembleru hranaté závorky kolem čísla (nebo názvu registru) znamenají, že číslo má být použito jako adresa k datům, která mají být použita. Použití adresy pro odkaz na data se nazývá indirekce. V tomto dalším příkladu se bez hranatých závorek do jiného registru, bx, skutečně načte hodnota 20.
Protože nebyla použita žádná indirekce, byla do registru vložena samotná hodnota.
Pokud se operandy (věci, které následují za mnemotechnickým znakem) objeví v opačném pořadí, instrukce, která něco načítá z paměti, to místo toho do paměti zapíše:
Zde paměť na adrese 1000h získá hodnotu ax. Pokud se tento příklad provede hned po předchozím, budou 2 bajty na adresách 1000h a 1001h tvořit 2 bajty celého čísla s hodnotou 20.
Instrukce, které provádějí matematické nebo logické operace.
Některé instrukce provádějí například odčítání nebo logické operace, jako například ne:
Příklad strojového kódu uvedený dříve v tomto článku by byl v jazyce assembleru:
Zde se sečtou hodnoty 42 a ax a výsledek se uloží zpět do ax. V assembleru x86 je také možné takto kombinovat přístup do paměti a matematickou operaci:
Tato instrukce přičte k ax hodnotu celého čísla uloženého v 2 bytech na 1000h a odpověď uloží do ax.
Tato instrukce vypočítá or obsahu registrů ax a bx a výsledek uloží zpět do ax.
Instrukce, které rozhodují o tom, jaká bude další instrukce.
Instrukce se obvykle provádějí v pořadí, v jakém se objevují v paměti, což je pořadí, v jakém jsou zapsány v kódu assembleru. Procesor je prostě vykonává jednu po druhé. Aby však procesory mohly dělat složité věci, musí provádět různé instrukce podle toho, jaká data jim byla zadána. Schopnost procesorů provádět různé instrukce v závislosti na tom, jaký je výsledek něčeho, se nazývá větvení. Instrukce, které rozhodují o tom, jaká má být další instrukce, se nazývají instrukce větvení.
V tomto příkladu předpokládejme, že někdo chce vypočítat množství barvy, které bude potřebovat k natření čtverce o určité délce strany. Z důvodu úspory z rozsahu mu však obchod s barvami neprodá menší množství barvy, než jaké je potřeba k natření čtverce o rozměrech 100 x 100 bodů.
Aby zjistili, jaké množství barvy budou potřebovat na základě délky čtverce, který chtějí vymalovat, vymyslí tento soubor kroků:
- od délky strany odečteme 100
- pokud je odpověď menší než nula, nastavte délku strany na 100
- vynásobte délku strany sebou samým
Tento algoritmus lze vyjádřit následujícím kódem, kde ax je délka strany.
Tento příklad přináší několik nových věcí, ale první dvě instrukce jsou známé. Zkopírují hodnotu ax do bx a poté od bx odečtou 100.
Jednou z nových věcí v tomto příkladu je tzv. label, což je pojem, který se vyskytuje v jazycích assembleru obecně. Štítek může být cokoli, co si programátor přeje (pokud to není název instrukce, což by asembler zmátlo). V tomto příkladu je štítkem 'continue'. Asembler jej interpretuje jako adresu instrukce. V tomto případě je to adresa mult ax.
Dalším novým konceptem jsou vlajky. V procesorech x86 mnoho instrukcí nastavuje v procesoru "příznaky", které může následující instrukce použít k rozhodnutí, co má udělat. V tomto případě, pokud bylo bx menší než 100, sub nastaví příznak, který říká, že výsledek byl menší než nula.
Další instrukcí je jge, což je zkratka pro 'Jump if Greater than or Equal to'. Jedná se o instrukci větvení. Pokud příznaky v procesoru určují, že výsledek byl větší než nebo roven nule, místo aby procesor prostě přešel na další instrukci, skočí na instrukci s označením continue, což je mul ax.
Tento příklad funguje dobře, ale většina programátorů by ho nenapsala. Instrukce odečtení správně nastavila příznak, ale zároveň změnila hodnotu, se kterou operuje, což vyžadovalo zkopírování ax do bx. Většina jazyků assembleru umožňuje použít porovnávací instrukce, které nemění žádný z předávaných argumentů, ale přesto správně nastavují příznaky, a assembler x86 není výjimkou.
Nyní se místo odečtení čísla 100 od ax, zjištění, zda je toto číslo menší než nula, a jeho přiřazení zpět k ax, ponechá ax beze změny. Příznaky jsou nastaveny stejným způsobem a skok se provede ve stejných situacích.
Vstup a výstup
Přestože vstup a výstup jsou základní součástí výpočetní techniky, v jazyce assembleru neexistuje jediný způsob, jak je provádět. Je to proto, že způsob, jakým vstupy a výstupy fungují, závisí na nastavení počítače a operačním systému, který na něm běží, nikoli pouze na tom, jaký má procesor. V části příkladů příklad Hello World používá volání operačního systému MS-DOS a příklad za ním volání systému BIOS.
V jazyku assembleru je možné provádět I/O. Jazyk assembleru může obecně vyjádřit cokoli, co je počítač schopen udělat. Nicméně i když v jazyce assembleru existují instrukce pro sčítání a větvení, které vždy provedou stejnou věc, neexistují v jazyce assembleru instrukce, které by vždy prováděly I/O.
Důležité je si uvědomit, že způsob fungování I/O není součástí žádného jazyka assembleru, protože není součástí fungování procesoru.
Jazyky assembleru a přenositelnost
Přestože assembler není přímo spouštěn procesorem, ale strojovým kódem, má s ním stále mnoho společného. Každá rodina procesorů podporuje jiné funkce, instrukce, pravidla pro to, co instrukce mohou dělat, a pravidla pro to, jaké kombinace instrukcí jsou kde povoleny. Z tohoto důvodu různé typy procesorů stále potřebují různé assemblerové jazyky.
Protože je každá verze jazyka assembleru vázána na rodinu procesorů, chybí mu něco, čemu se říká přenositelnost. Něco, co má přenositelnost nebo je přenosné, lze snadno přenést z jednoho typu počítače na jiný. Zatímco jiné typy programovacích jazyků jsou přenositelné, jazyk assembler obecně přenositelný není.
Jazyk assembleru a vysokoúrovňové jazyky
Ačkoli jazyk assembleru umožňuje snadno využívat všechny funkce procesoru, pro moderní softwarové projekty se z několika důvodů nepoužívá:
- Vyjádřit jednoduchý program v assembleru vyžaduje velké úsilí.
- Ačkoli jazyk assembleru není tak náchylný k chybám jako strojový kód, stále nabízí jen velmi malou ochranu proti chybám. Téměř všechny assemblerové jazyky nevynucují typovou bezpečnost.
- Jazyk assembleru nepodporuje správné programovací postupy, jako je modularita.
- I když je každá jednotlivá instrukce jazyka assembleru snadno pochopitelná, je těžké určit, jaký byl záměr programátora, který ji napsal. Ve skutečnosti je jazyk assembleru programu tak těžko srozumitelný, že si firmy nedělají starosti s tím, že by lidé disassemblovali (získávali jazyk assembleru) jejich programy.
V důsledku těchto nevýhod se pro většinu projektů používají vysokoúrovňové jazyky, jako je Pascal, C a C++. Umožňují programátorům vyjádřit své myšlenky příměji, místo aby se museli starat o to, co má procesor na každém kroku dělat. Říká se jim vysokoúrovňové, protože myšlenky, které může programátor vyjádřit ve stejném množství kódu, jsou složitější.
Programátoři píšící kód v kompilovaných vysokoúrovňových jazycích používají k převodu kódu do jazyka assembleru program zvaný kompilátor. Kompilátory jsou mnohem náročnější na psaní než assemblery. Vysokoúrovňové jazyky také ne vždy umožňují programátorům využívat všechny funkce procesoru. Je to proto, že vysokoúrovňové jazyky jsou navrženy tak, aby podporovaly všechny rodiny procesorů. Na rozdíl od asemblerových jazyků, které podporují pouze jeden typ procesoru, jsou vysokoúrovňové jazyky přenositelné.
Přestože jsou překladače složitější než assemblery, díky desetiletím vývoje a výzkumu jsou velmi dobré. V současné době již není mnoho důvodů používat pro většinu projektů jazyk assembler, protože kompilátory obvykle dokáží vymyslet, jak vyjádřit programy v jazyce assembler stejně dobře nebo lépe než programátoři.
Příklad programů
Program Hello World napsaný v assembleru x86:
Funkce, která vytiskne číslo na obrazovku pomocí přerušení systému BIOS napsaného v assembleru NASM x86. Modulární kód je možné napsat v assembleru, ale vyžaduje to další úsilí. Všimněte si, že vše, co je na řádku za středníkem, je komentář a assembler to ignoruje. Uvádění komentářů v kódu v assembleru je velmi důležité, protože rozsáhlé programy v assembleru jsou velmi těžko srozumitelné.
Otázky a odpovědi
Otázka: Co je to jazyk assembleru?
A: Jazyk assembleru je programovací jazyk, který lze použít k přímému zadání úkolů počítači. Je téměř stejný jako strojový kód, kterému počítač rozumí, jen místo čísel používá slova.
Otázka: Jak počítač rozumí programu v assembleru?
Odpověď: Počítač ve skutečnosti nemůže přímo rozumět programu v assembleru, ale může snadno změnit program na strojový kód tím, že nahradí slova programu čísly, která znamenají. Tento proces se provádí pomocí assembleru.
Otázka: Co jsou instrukce v jazyce assembleru?
Odpověď: Instrukce v jazyce assembleru jsou malé úlohy, které počítač provádí při spouštění programu. Nazývají se instrukce, protože dávají počítači pokyny, co má dělat. Část počítače, která je zodpovědná za provádění těchto instrukcí, se nazývá procesor.
Otázka: Jaký typ programovacího jazyka je assembler?
Odpověď: Jazyk assembler je nízkoúrovňový programovací jazyk, což znamená, že může být použit pouze k provádění jednoduchých úloh, kterým počítač přímo rozumí. Aby bylo možné provádět složitější úlohy, je třeba každou úlohu rozdělit na jednotlivé komponenty a zadat instrukce pro každou komponentu zvlášť.
Otázka: Jak se liší od vysokoúrovňových jazyků?
Odpověď: Vysokoúrovňové jazyky mohou mít jednotlivé příkazy, jako například PRINT "Hello, world!", které počítači řeknou, aby všechny tyto malé úlohy provedl automaticky, aniž by je bylo nutné zadávat jednotlivě, jak by bylo nutné u programu v assembleru. Díky tomu jsou vysokoúrovňové jazyky pro člověka snáze čitelné a srozumitelné než programy v assembleru složené z mnoha jednotlivých příkazů.
Otázka: Proč může být pro člověka obtížné číst program v assembleru?
Odpověď: Protože pro složitou úlohu, jako je vytištění něčeho na obrazovku nebo provedení výpočtů na souborech dat - což jsou věci, které se zdají být velmi základní a jednoduché, když jsou vyjádřeny přirozeným lidským jazykem - musí být specifikováno mnoho jednotlivých instrukcí, takže může existovat mnoho řádků kódu tvořících jednu instrukci, což lidem, kteří nevědí, jak počítače vnitřně fungují na tak nízké úrovni, ztěžuje sledování a interpretaci toho, co se v nich děje.