Učebnice Assembleru 86

Volání podprogramů

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.

Ukládání parametrů do zásobníku

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.

Samotné volání podprogramu

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_);

Návrat hodnoty z funkce

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.

Směr