V úvodu jsem upozornil na to, že využití vkládaného assembleru je v tvorbě podprogramů. Předem si ale musíme ukázat, jak se podprogramy volají.
Volání podprogramu spočívá v uložení parametrů do zásobníku a změně adresy v registru IP (čítač instrukcí) na adresu podprogramu s tím, že je uschována adresa odkud provádíme volání (to aby procesor věděl kam se má vrátit). Parametry do zásobníku ukládáme my, zbytek zařídí instrukce CALL.
V hlavičce procedury (nebo funkce) najdeme téměř vždy definici parametrů volaných:
Například: procedure
soucet (a,b:word;var c:word);
je definice procedury s názvem součet s parametry a, b
volanými hodnotou a c volaným odkazem. Při volání této
procedury z některé části programu psaném v Pascalu na
místa a, b zapíšeme konkrétní hodnoty (nebo proměnné (ty
ale podprogram nezmění) s těmito hodnotami) a na místo c
zapíšeme proměnnou, ve které najdeme hodnotu po provedení
procedury (např. soucet (1,3,promenna_c);
). Z místa volání předáváme parametry
do podprogramů vždy přes zásobník v pořadí definice v
hlavičce podprogramu. Do zásobníku před voláním procedury
ukládáme odlišně u parametrů volaných hodnotou a odkazem.
Musíme rozlišovat volání blízkého podprogramu a vzdáleného. Za vzdálený v tomto případě považujeme podprogram s adresou v odlišném segmentu. I když se pro programátora nic nemění je dobré vědět, že při vzdáleném volání se mění nejen IP, ale i CS. Označení místa skoku nese tedy navíc informaci o segmentové adrese. Skok do podprogramu zajistí instrukce
Ukončení samotného podprogramu zajistí instrukce
Jednoduše napíšeme instrukci CALL se jménem podprogramu (tedy procedury nebo funkce). Ostatní zařídí překladač, který zjistí, jestli se jedná o blízké nebo vzdálené volání. Podle toho dosadí adresu. Návrat si opět zařídí překladač při ukončení podprogramu.
Příklad:
{$G+}
uses crt;
procedure pocitej (a,b:word;var c,d:word);
begin
c:=a+b;
d:=a-b;
end;
var a_,b_,c_,d_:word;
begin
a_:=40;
b_:=5;
clrscr;
asm
PUSH
a_
{proceduře posíláme hodnotu a_}
PUSH
b_
{proceduře posíláme hodnotu b_}
LEA
DI,c_
{zjistíme adresu proměnné c_}
PUSH
DS
{do zásobníku segment adresy c_}
PUSH
DI
{do zásobníku offset adresy c_}
LEA
DI,d_
{to samé pro d_}
PUSH
DS
{stejný segment}
PUSH
DI
{offset d_}
CALL
pocitej {a
zavoláme počítej}
end;
writeln (a_,'+(-)',b_,'=',c_,'(',d_,')');
readkey;
end.
Stejnou posloupnost instrukcí jako blok asm v tomto programu provede řádek počítej (a_,b_,c_,d_);
Funkce je podprogram, který vrací jednu hodnotu typu uvedeného v záhlaví. Vracenou hodnotu zjistíme po návratu z funkce vždy v registrech:
Pokud funkce vrací řetězec, musí být volána i s adresou místa, kam má výsledný řetězec zapsat.
Příklad:
{$G+}
uses crt;
function bez1 (a:word):word;
begin
bez1:=a-1;
end;
var a_,c_:word;
begin
a_:=40;
clrscr;
asm
PUSH a_ {posíláme hodnotu a_}
CALL bez1 {zavoláme }
MOV c_,AX {slovo si vyzvedneme v registru AX}
end;
writeln (a_,'-1=',c_);
readkey;
end.