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:

  • hodnotou - podprogram jejich hodnoty pouze využívá
  • odkazem - podprogram je může číst a může do nich i zapsat

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.

  • Při volání hodnotou
    Uložíme konkrétní hodnoty (přečtené třeba i z paměti). Vzhledem k organizaci zásobníku jsou parametry volané hodnotou uloženy po slovech následovně:
    • parametry o délce jedné slabiky (byte, shortint, char, boolean) - obsadí celé slovo (pamětí nešetří)
    • parametry o délce jednoho slova (word, integer) - obsadí slovo
    • parametry o délce dvojslova (pointer, longint) - obsadí dvě slova (ukazatel je adresa, do zásobníku tedy napřed uložíme segmentovou a pak offsetovou část adresy)
    • parametry o délce 6 slabik (real) - obsadí v zásovníku tři slova
    • parametry delší (řetězce, množina, pole, záznamy) - se ukládají jako ukazatele na hodnotu.
  • Při volání odkazem
    Uložíme celou adresu místa (tedy segment i offset) odkud se má hodnota číst nebo kam se má zapsat (to je vlastně obsah ukazatele na paměťové místo).

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

  • CALL adresa - na vrchol zásobníku ulož obsah (CS při vzdáleném volání a) IP a naplň tyto registry adresou uvedenou v parametru (pro nás slovo adresa nahradíme názvem podprogramu)

Ukončení samotného podprogramu zajistí instrukce

  • RET[F] - z vrcholu zásobníku vezmi adresy a dosaď je do (CS a) IP Volání podprogramů je tedy jednoduché.

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:

  • AL - funkční hodnota o velikosti slabiky
  • AX - funkční hodnota o velikosti slova
  • DX, AX - funkční hodnota o velikosti dvojslova (u ukazatele DX - segment, AX - offset)
  • DX, BX, AX - funkční hodnota typu real

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