Na pochopenie fungovania programu v jazyku assembler je potrebné pochopiť, ako sú počítače organizované, ako zdanlivo fungujú na veľmi nízkej úrovni. Na najjednoduchšej úrovni majú počítače tri hlavné časti:
- hlavná pamäť alebo pamäť RAM, v ktorej sú uložené údaje a inštrukcie,
- procesor, ktorý spracúva údaje vykonávaním inštrukcií, a
- vstup a výstup (niekedy skrátene I/O), ktoré umožňujú počítaču komunikovať s vonkajším svetom a ukladať údaje mimo hlavnej pamäte, aby ich mohol neskôr získať späť.
Hlavná pamäť
Vo väčšine počítačov je pamäť rozdelená na bajty. Každý bajt obsahuje 8 bitov. Každý bajt v pamäti má aj adresu, čo je číslo, ktoré hovorí, kde sa daný bajt v pamäti nachádza. Prvý bajt v pamäti má adresu 0, ďalší má adresu 1 atď. Rozdelenie pamäte na bajty umožňuje jej adresovanie na bajty, pretože každý bajt dostane jedinečnú adresu. Adresy bajtových pamätí sa nemôžu použiť na označenie jedného bitu bajtu. Bajt je najmenšia časť pamäte, ktorú možno adresovať.
Aj keď sa adresa vzťahuje na konkrétny bajt v pamäti, procesory umožňujú používať niekoľko bajtov pamäte za sebou. Najčastejšie sa táto funkcia využíva na použitie 2 alebo 4 bajtov v rade na reprezentáciu čísla, zvyčajne celého čísla. Na reprezentáciu celých čísel sa niekedy používajú aj jednotlivé bajty, ale keďže majú iba 8 bitov, môžu obsahovať iba 2 8alebo 256 rôznych možných hodnôt. Použitím 2 alebo 4 bajtov v riadku sa zvýši počet rôznych možných hodnôt na 2 16, 65536, resp. 2 32, 4294967296.
Keď program používa bajt alebo niekoľko bajtov v rade na reprezentáciu niečoho, ako je písmeno, číslo alebo čokoľvek iné, tieto bajty sa nazývajú objekt, pretože sú súčasťou tej istej veci. Aj keď sú všetky objekty uložené v rovnakých bajtoch pamäte, zaobchádza sa s nimi, akoby mali "typ", ktorý hovorí, ako sa majú bajty chápať: buď ako celé číslo, alebo znak, alebo nejaký iný typ (napríklad neceločíselná hodnota). Strojový kód možno tiež považovať za typ, ktorý sa interpretuje ako inštrukcie. Pojem typ je veľmi, veľmi dôležitý, pretože definuje, aké veci sa môžu a nemôžu robiť s objektom a ako sa majú interpretovať bajty objektu. Napríklad nie je platné uložiť záporné číslo do objektu s kladným číslom a nie je platné uložiť zlomok do objektu s celým číslom.
Adresa, ktorá ukazuje na viacbajtový objekt (je jeho adresou), je adresou prvého bajtu tohto objektu - bajtu, ktorý má najnižšiu adresu. Na okraj treba poznamenať, že podľa adresy objektu nemožno určiť jeho typ - a dokonca ani jeho veľkosť. V skutočnosti nemôžete ani zistiť, aký typ objektu je, keď sa naň pozriete. Program v jazyku assembler musí sledovať, na ktorých pamäťových adresách sa nachádzajú ktoré objekty a aké sú tieto objekty veľké. Program, ktorý to robí, je typovo bezpečný, pretože robí s objektmi len také veci, ktoré sú bezpečné pre ich typ. Program, ktorý to nerobí, pravdepodobne nebude fungovať správne. Všimnite si, že väčšina programov v skutočnosti explicitne neukladá, aký je typ objektu, len dôsledne pristupuje k objektom - s tým istým objektom sa vždy zaobchádza ako s rovnakým typom.
Procesor
Procesor vykonáva inštrukcie, ktoré sú uložené ako strojový kód v hlavnej pamäti. Okrem možnosti prístupu do pamäte na ukladanie dát má väčšina procesorov niekoľko malých, rýchlych priestorov s pevnou veľkosťou na uchovávanie objektov, s ktorými sa práve pracuje. Tieto priestory sa nazývajú registre. Procesory zvyčajne vykonávajú tri typy inštrukcií, hoci niektoré inštrukcie môžu byť kombináciou týchto typov. Nižšie je uvedených niekoľko príkladov jednotlivých typov v jazyku assembly x86.
Inštrukcie, ktoré čítajú alebo zapisujú do pamäte
Nasledujúca inštrukcia jazyka x86 prečíta (načíta) dvojbajtový objekt z bajtu na adrese 4096 (0x1000 v šestnástkovej sústave) do 16-bitového registra s názvom 'ax':
mov ax, [1000h]
V tomto jazyku assembleru hranaté zátvorky okolo čísla (alebo názvu registra) znamenajú, že číslo sa má použiť ako adresa na údaje, ktoré sa majú použiť. Použitie adresy na poukázanie na údaje sa nazýva indirekcia. V tomto ďalšom príklade sa bez hranatých zátvoriek do iného registra, bx, v skutočnosti načíta hodnota 20.
mov bx, 20
Keďže sa nepoužilo žiadne presmerovanie, do registra sa vložila samotná hodnota.
Ak sa operandy (veci, ktoré nasledujú za mnemotechnickým príkazom) objavia v opačnom poradí, inštrukcia, ktorá niečo načíta z pamäte, to namiesto toho zapíše do pamäte:
mov [1000h], ax
Tu pamäť na adrese 1000h dostane hodnotu ax. Ak sa tento príklad vykoná hneď po predchádzajúcom príklade, 2 bajty na adresách 1000h a 1001h budú 2 bajty celého čísla s hodnotou 20.
inštrukcie, ktoré vykonávajú matematické alebo logické operácie
Niektoré inštrukcie vykonávajú veci ako odčítanie alebo logické operácie ako nie:
Príklad strojového kódu uvedený v tomto článku by bol v jazyku assembleru:
pridať sekeru, 42
Tu sa sčítajú hodnoty 42 a ax a výsledok sa uloží späť do ax. V assembleri x86 je tiež možné takto kombinovať prístup do pamäte a matematickú operáciu:
pridať ax, [1000h]
Táto inštrukcia pripočíta hodnotu 2 bajtov celého čísla uloženého na 1000h k ax a odpoveď uloží do ax.
alebo ax, bx
Táto inštrukcia vypočíta or obsahu registrov ax a bx a výsledok uloží späť do ax.
Inštrukcie, ktoré rozhodujú o tom, aká bude ďalšia inštrukcia
Inštrukcie sa zvyčajne vykonávajú v poradí, v akom sa objavujú v pamäti, teda v poradí, v akom sú napísané v assembleri. Procesor ich jednoducho vykonáva jednu po druhej. Aby však procesory mohli robiť zložité veci, musia vykonávať rôzne inštrukcie na základe toho, aké údaje im boli zadané. Schopnosť procesorov vykonávať rôzne inštrukcie v závislosti od toho, aký je výsledok niečoho, sa nazýva vetvenie. Inštrukcie, ktoré rozhodujú o tom, aká má byť ďalšia inštrukcia, sa nazývajú inštrukcie vetvenia.
V tomto príklade predpokladajme, že niekto chce vypočítať množstvo farby, ktoré bude potrebovať na vymaľovanie štvorca s určitou dĺžkou strany. Z dôvodu úspory z rozsahu mu však obchod s farbami nepredá menšie množstvo farby, ako je potrebné na natretie štvorca s rozmermi 100 x 100.
Aby zistili množstvo farby, ktoré budú potrebovať na základe dĺžky štvorca, ktorý chcú vymaľovať, vymysleli tento súbor krokov:
- od dĺžky strany odpočítajte 100
- ak je odpoveď menšia ako nula, nastavte dĺžku strany na 100
- vynásobte dĺžku strany samou sebou
Tento algoritmus možno vyjadriť nasledujúcim kódom, kde ax je dĺžka strany.
mov bx, ax sub bx, 100 jge continue mov ax, 100 continue: mul ax
Tento príklad zavádza niekoľko nových vecí, ale prvé dva príkazy sú známe. Skopírujú hodnotu ax do bx a potom od bx odčítajú 100.
Jedna z nových vecí v tomto príklade sa nazýva label, čo je pojem, ktorý sa vo všeobecnosti vyskytuje v jazykoch assembleru. Štítkom môže byť čokoľvek, čo si programátor želá (pokiaľ to nie je názov inštrukcie, čo by asembler zmiatlo). V tomto príklade je značkou "continue". Asembler ho interpretuje ako adresu inštrukcie. V tomto prípade je to adresa mult ax.
Ďalším novým konceptom sú vlajky. V procesoroch x86 mnohé inštrukcie nastavujú v procesore "príznaky", ktoré môže ďalšia inštrukcia použiť na rozhodnutie, čo má urobiť. V tomto prípade, ak bx bolo menšie ako 100, sub nastaví príznak, ktorý hovorí, že výsledok bol menší ako nula.
Ďalšou inštrukciou je jge, čo je skratka pre "Jump if Greater than or Equal to". Je to inštrukcia vetvenia. Ak príznaky v procesore určia, že výsledok bol väčší alebo rovný nule, namiesto toho, aby procesor jednoducho prešiel na ďalšiu inštrukciu, skočí na inštrukciu na značke continue, čo je mul ax.
Tento príklad funguje dobre, ale väčšina programátorov by ho nenapísala. Inštrukcia odčítania správne nastavila príznak, ale zároveň zmenila hodnotu, s ktorou operuje, čo si vyžadovalo skopírovanie ax do bx. Väčšina jazykov assembleru umožňuje použitie porovnávacích inštrukcií, ktoré nemenia žiadny z odovzdávaných argumentov, ale napriek tomu správne nastavujú príznaky, a assembler x86 nie je výnimkou.
cmp ax, 100 jge continue mov ax, 100 continue: mul ax
Namiesto toho, aby sme od ax odčítali 100, zistili, či je toto číslo menšie ako nula, a priradili ho späť k ax, ax zostane nezmenené. Príznaky sú stále nastavené rovnakým spôsobom a skok sa vykoná v rovnakých situáciách.
Vstup a výstup
Hoci vstup a výstup sú základnou súčasťou počítačov, v jazyku assembleru neexistuje jediný spôsob, ako ich realizovať. Je to preto, že spôsob fungovania vstupov a výstupov závisí od nastavenia počítača a operačného systému, ktorý na ňom beží, nielen od toho, aký má procesor. V časti s príkladmi príklad Hello World používa volania operačného systému MS-DOS a príklad po ňom používa volania systému BIOS.
V jazyku assembleru je možné vykonávať I/O. V jazyku assembler sa dá všeobecne vyjadriť všetko, čo je počítač schopný urobiť. Avšak napriek tomu, že v jazyku assembleru existujú inštrukcie na sčítanie a vetvenie, ktoré vždy vykonajú tú istú vec, v jazyku assembleru neexistujú inštrukcie, ktoré by vždy vykonávali I/O.
Dôležité je uvedomiť si, že spôsob, akým funguje I/O, nie je súčasťou žiadneho jazyka assembleru, pretože nie je súčasťou fungovania procesora.