Podprogramy
1. Volání a základní použití
Jednoduché volání podprogramu je pomocí instrukce call jmeno, návrat z podprogramu je pomocí ret.
Podprogramy pište až za ukončovací instrukci programu, aby se náhodou nevykonaly automaticky po tom, co hlavní program skončí.
Asembler nezná nic jako lokální proměnné. Pokud používáte registry uvnitř podprogramu, tak se ujistěte, že si nepřepíšete nějakou užitečnou hodnotu, která v nich byla uložena. Pokud chcete, aby váš podprogram byl univerzální, tak můžete registry, které v něm potřebujete, na začátku podprogramu "uklidit" na zásobník a na konci je zase obnovit.
Na zásobník můžete "uklízet" pomocí instrukce push registr, vybírat/obnovovat ze zásobníku poté pomocí pop registr. Jde o zásobník – nezapomeňte, že co jde dovnitř jako poslední, musí jít ven jako první. Nezapomeňte vybrat vše, co jste uklidili a nic navíc, jinak se instrukce ret nebude umět vrátit do hlavního programu (call totiž ukládá návratovou hodnotu také na zásobník a ret ji tam hledá, takže jim tam nesmíte udělat nepořádek).
Následující kód obsahuje příklad podprogramu s "úklidem":
; inicializace a kod hlavniho programu
call proc ; volani podprogramu se jmenem <proc> (<proc> je take navesti). Velmi podobne jako jmp,
; ale ulozi si adresu aktualni instrukce,
; takze je mozny navrat zpatky pomoci instrukce ret.
; dalsi kod hlavniho programu
jmp PC ; skok na aktualni radek 1
proc: ; sem skoci ten "call proc" o par radku vyse
push r16 ; uklizeni r16 2
in r16, SREG ; 2
push r16 ; uklizeni status registru 2
push r10 ; uklizeni libovolneho dalsiho registru
; libovolny kod s registrem r10 a r16 2
; obnoveni probiha v opacnem poradi
pop r10 ; obnoveni libovolneho dalsiho registru
pop r16 ; 2
out SREG, r16; obnoveni status registru 2
pop r16 ; obnoveni r16 2
ret ; navrat z podprogramu (na radek za posledni call)- PC je návěští, které automaticky ukazuje na aktuální řádek. jmp PC je de facto malá nekonečná smyčka, díky které program nepokračuje dál do kódu, který chceme používat jako podprogram proc.
- Pro podprogramy není úklid status registru bezpodmínečně nutný (na rozdíl od přerušení). Pro úklid status registru (SREG) není nutně potřeba používat r16, ale když použijete jiný registr, tak se ujistěte, že jste ho použili na všech řádkách označených poznámkou <2>.
2. Předávání parametrů
Pokud byste chtěli vašemu podprogramu předávat parametry, tak je k dispozici několik možností:
2.1. Přes registry
Výhody: rychlé, jednoduché
Nevýhody: omezený počet registrů
Tímto způsobem funguje např. show_char.
Před voláním podprogramu se jednoduše uloží do vybraných registrů data a v podprogramu se použijí. Asembler nezná nic jako lokální proměnné, takže data v registrech jsou vidět i v podprogramech.
2.2. Na zásobníku
Výhody: flexibilní
Nevýhody: pomalejší, komplikovanější výběr, nekompatibilní s výše zmíněným úklidem
; kod hlavniho programu
push r0 ; ulozeni parametru na zasobnik 1
call proc ; volani podprogramu se jmenem <proc> (<proc> je take navesti). Velmi podobne jako jmp,
; ale ulozi si adresu aktualni instrukce,
; takze je mozny navrat zpatky pomoci instrukce ret.
; dalsi kod hlavniho programu
jmp PC ; skok na aktualni radek
proc: ; sem skoci ten "call proc" o par radku vyse
pop r30 ; vyber prvni casti navratove adresy 2
pop r31 ; vyber druhe casti navratove adresy 2
pop r20 ; vyber ulozeneho parametru 2
push r31 ; zpetne ulozeni prvni casti navratove adresy
push r30 ; zpetne ulozeni druhe casti navratove adresy
; zde muzeme teprve zahajit uklid
ret ; navrat z podprogramu (na radek za posledni call)- Není nutné použít zrovna r0 – do zásobníku lze pushnout libovolný registr.
- Pro vybrání parametru potřebujeme nejprve vybrat 16bitovou adresu uloženou callem. Dojde k nenávratném přepsání hodnot tří registrů (návratová adresa a vybíraný parametr), které není možno "uklidit" na zásobník. Jediná možnost by byla, že by se před prvním popem registry uložily do datové paměti, ale to už je jednodušší si přes datovou paměť poslat rovnou parametr.
2.3. V datové paměti
Výhody: "neomezená" velikost (2 kB paměti přece musí stačit každému), kompatibilní s výše zmíněným úklidem
Nevýhody: pomalejší, o něco komplikovanější výběr (ale ne tak jako u předávání na zásobníku)
Pro předání v datové paměti je možné využít dva způsoby:
- Předáním přímé hodnoty:
Před voláním podprogramu se uloží do vybrané pozice v paměti data a v podprogramu se vyberou a použijí. Princip je tedy podobný jako u předávání přes registry, ale paměti je k dispozici více a je výrazně menší šance, že v ní dojde k přepsání jiných důležitých dat. - Předáním ukazatele:
Vhodné zejména pro předávání polí. Před voláním podprogramu se uloží do vybraných registrů, nebo pozice v paměti ukazatel na předávaná data a v podprogramu se adresa najde, vybere a poté se mohou vybrat a použít i samotná data.