Skoky a podprogramy - M8C
Obsah
Podprogramy
Volanie podprogramu spočíva v uložení parametrov do zásobníka a zmene adresy v registri IP (čítač inštrukcií) na adresu podprogramu s tým, že je uschovaná adresa odkiaľ prevádzame volanie (to aby procesor vedel kam sa má vrátiť). Parametre do zásobníku ukladáme my, zvyšok zariadi inštrukcia CALL.
Ukladanie parametrov do zásobníka
V hlavičke procedúry (alebo funkcie) nájdeme takmer vždy definíciu parametrov volaných:
- hodnotou - podprogram ich hodnoty iba využíva
- odkazom - podprogram ich môže čítať a môže do nich i zapísať
Pri volaní hodnotou
Uložíme konkrétne hodnoty (prečítané napr. i z pamäti). Vzhľadom k organizácii zásobníka sú parametre volané hodnotou uložené po slovách nasledovne:
- parametre o dĺžke jednej slabiky (byte, short int, char, bool) - obsadia celé slovo (pamäťou nešetria)
- parametre o dĺžke jedného slova (word, int) - obsadia slovo
- parametre o dĺžke dvojslova (pointer, long int) - obsadia dve slova (ukazovateľ je adresa, do zásobníka teda najskôr uložíme segmentovú a potom offsetovú časť adresy)
- parametre o dĺžke 6 slabík (float) - obsadia v zásobníku tri slová
- parametre dlhšie (reťazce, množina, pole, záznamy) - sa ukladajú ako ukazovatele na hodnotu.
Pri volaní odkazom
Uložíme celú adresu miesta (teda segment i offset) odkiaľ sa má hodnota čítať alebo kam sa má zapísať (to je vlastne obsah ukazovateľa na pamäťové miesto).
Samotné volanie podprogramu
Musíme rozlišovať volanie blízkeho podprogramu a vzdialeného. Za vzdialený v tomto prípade považujeme podprogram s adresou v odlišnom segmente. I keď sa pre programátora nič nemení je dobré vedieť, že pri vzdialenom volaní sa mení nie len IP, ale i CS. Označenie miesta skoku nesie teda naviac informáciu o segmentovej adrese. Skok do podprogramu zaistí inštrukcia
CALL
adresa - na vrchol zásobníka ulož obsah (CS pri vzdialenom volaní a) IP a naplň tieto registre adresou uvedenou v parametri (pre nás slovo adresa nahradíme názvom podprogramu)
CALL - rozsah podprogram 2 kB, 11-bitová adresa. LCALL - rozsah podprogram 64 kB, 16-bitová adresa.
Ukončení samotného podprogramu zaistí inštrukcia
RET[F]
z vrcholu zásobníka vezmi adresy a dosaď ich do (CS a) IP Volanie podprogramov je teda jednoduché.
Jednoducho napíšeme inštrukciu CALL s menom podprogramu (teda procedúry alebo funkcie). Ostatné zariadi prekladač, ktorý zistí, či sa jedná o blízke alebo vzdialené volanie. Podľa toho dosadí adresu. Návrat si opäť zariadi prekladač pri ukončení podprogramu.
Za volaním programu musíme uvolniť zásobník, teda musíme vybrať toľko hodnôt, koľko sme ich vložili pred volaním podprogramu (počet parametrov podprogramu).
Návrat hodnoty z funkcie
Funkcia je podprogram, ktorý vracia jednu hodnotu typu uvedeného v záhlaví. Vracanú hodnotu zistíme po návrate z funkcie vždy v registroch:
- AL - funkčná hodnota o veľkosti slabiky
- AX - funkčná hodnota o veľkosti slova
- DX, AX - funkčná hodnota o veľkosti dvojslova (pri ukazovateli DX - segment, AX - offset)
- DX, BX, AX - funkčná hodnota typu float
Pokiaľ funkcia vracia reťazec, musí byť volaná i s adresou miesta, kam má výsledný reťazec zapísať.
Podprogram 'sčítaj'
Vytvoríme podprogram sčítaj. Najskôr musíme vyriešiť odovzdávanie parametrov podprogramu. Vo vyšších programovacích jazykoch sa pre odovzdanie parametra používa zásobník, my si zatiaľ vystačíme s dohodnutými registrami. Akonáhle vykonáme inštrukciu pre sčítanie ADD, bude jedna hodnota operanda prepísaná výsledkom. Využijeme zásobník a hodnotu uloženú v danom registri si uložíme a neskôr opäť obnovíme.
scitaj:
push eax; uložíme hodnotu registra EAX na zásobník
add eax, EBX; sčítame EAX a EBX, výsledok bude v EAX
mov ecx, eax; výsledok z EAX uložíme do ECX
pop eax; obnovíme pôvodnú hodnotu v registri EAX
ret ; návrat z podprogramu.
Podprogram scitaj vyskúšame s hodnotami 4 a 8.
mov eax, 4; do EAX uložíme 4
mov EBX, 8; EBX = 8
call scitaj ; zavoláme podprogram scitaj
;Výsledok bude uložený v registri ECX a bude JIRRA.
; hodnota ECX = OxOOOOOOOC
Skoky
Nepodmienený skok
Je to nepodmienený skok na iné miesto programu. To musí byť označené návestím. Za inštrukciou skoku je potom uvedený jeho názov.
JMP
navestie - urob skok programu na návestie (v skutočnosti sa len zmení obsah čítača inštrukcií IP, prípadne CS pri vzdialenom skoku) V programe potom nepodmienený skok vyzerá takto:
navestie: inštrukcia na ktorú bude odkaz
.
.
JMP navestie
Ak skoky používame, hrozí vždy nebezpečie, že sa program zacyklí (a nikdy neskončí). Preto je dôležité si vždy rozmyslieť, za akých okolností by k tejto kolízii mohlo dôjsť.
Podmienený skok
Jedná sa o skok podmienený stavom jedného alebo viac, bitov registri príznakov F. Len týmto spôsobom je možné prevádzať v assembleri priame vetvenie programu. Pred inštrukciou podmieneného skoku preto vždy prevedieme inštrukciu, ktorá použitý príznak nastaví. V prípade, že nie je splnená podmienka skoku, pokračuje program ďalej, ako by sa nič nedialo. Inštrukcie podmieneného skoku začínajú vždy písmenkom J. Za ním je skratka udávajúca na akých bitoch registru F je skok závislý.
- JZ návestie - skok na návestie pri ZF = 1
- JNZ návestie - skok na návestie pri ZF = 0
- JC návestie - skok na návestie pri CF = 1
- JNC návestie - skok na návestie pri CF = 0
Pri hľadaní inštrukcie podmieneného skoku musíme myslieť na to, za akých okolností chceme skok vykonať. K tomu je tiež dobré si uvedomiť:
- A < B => A - B < 0 => SF = 1
- A = B => A - B = 0 => ZF = 1
- A > B => A - B > 0 => SF = 0
Rozdiel čísel v tomto prípade prevedieme najlepšie inštrukciou CMP. Pre tvorbu cyklu môžeme použiť jeden z registrov, ktorý si pre krokovaciu premennú vyčleníme. Jednoduchý cyklus potom vytvoríme podmieneným skokom:
MOV CL, 10 // do registra CL dosaď 10, počet krokov
nav:
DEC CL // odčítaj od CL číslo 1
JNZ nav // ak nie je nula skoč na návestie
Program opakuje skok dokiaľ nie je v registri CL nulový výsledok.