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':
mov ax, [1000h]
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.
mov bx, 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:
mov [1000h], ax
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:
přidat sekeru, 42
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:
add ax, [1000h]
Tato instrukce přičte k ax hodnotu celého čísla uloženého v 2 bytech na 1000h a odpověď uloží do ax.
nebo ax, bx
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.
mov bx, ax sub bx, 100 jge continue mov ax, 100 continue: mul ax
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.
cmp ax, 100 jge continue mov ax, 100 continue: mul ax
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.