V úvodu som upozornil na to, že využitie vkladaného assembleru je v tvorbe podprogramov. Najskôr
si ale musíme ukázať, ako sa podprogramy volajú.
Volaní podprogramu spočíva v uložení parametrov do zásobníka a zmene adresy v registru 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ť
Napríklad: void sucet (word a, word b, word &c)
je definícia procedúry s názvom súčet s parametrami a, b volanými hodnotou
a c volaným odkazom. Pri volaní tejto procedúry z niektorej časti programu
písanom v C++ na miesta a, b zapíšeme konkrétne hodnoty (alebo premenné
(tie ale podprogram nezmení) s týmito hodnotami) a na miesto c zapíšeme
premennú, v ktorej nájdeme hodnotu po prevedení procedúry (napr. sucet
(1,3,premenna_c);).
Z miesta volania predávame parametre do podprogramov vždy cez zásobník
v opačnom poradí ako je v definícii hlavičky podprogramu (prvá premenná
bude na vrchole zásobníka). Do zásobníka pred volaním procedúry ukladáme
odlišne parametre volané hodnotou a odkazom.
- 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)
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).
#include <conio.h>
#include <stdio.h>
typedef unsigned int word;
word wa,wb,wc,wd;
void pocitaj(word a, word b, word &c, word &d) {
c = a + b;
d = a - b;
}
void *proc_pocitaj=pocitaj;
void main() {
wa = 40;
wb = 5;
clrscr();
asm {
LEA DI,wd // zistíme adresu premennej wd
PUSH DI // offset premennej wd
LEA DI,wc // zistíme adresu premennej wc
PUSH DI // offset premennej wc
PUSH wb // procedúre posielame hodnotu wb
PUSH wa // procedúre posielame hodnotu wa
CALL proc_pocitaj // a zavoláme pocitaj
MOV cx, 4 // do zásobníka sme vložili 4 hodnoty
REP POP ax // zásobník musíme vyprázdniť
}
printf("%d+(-)%d=%d(%d)", wa,wb,wc,wd);
getchar();
}
Rovnakú postupnosť
inštrukcií ako blok asm v tomto programe prevedie riadok pocitaj(aa,bb,cc,dd);
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ť.
#include <conio.h>
#include <stdio.h>
#include <process.h>
typedef unsigned int word;
word wa, wc;
word minus(word a) {
return (a-1);
}
void *funk_minus = minus;
void main() {
wa = 40;
clrscr();
asm {
PUSH wa // posielame hodnotu wa
CALL funk_minus // zavoláme funkciu
MOV wc, AX // slovo si vyzdvihneme v registri AX
POP AX // vyprázdnime zásobník
}
printf("\n%d-1=%d",wa,wc);
getchar();
}