Laborator 8 - Suport teoretic

Apeluri de functii sistem

Biblioteci de functii (function libraries)

Chiar daca limbajul de asamblare foloseste in mod direct componentele hardware ale sistemului, exista portiuni de cod utilizate frecvent, care ar fi impractic sa fie scrise de catre programator de fiecare data. De exemplu, comunicarea cu dispozitivele de intrare/iesire, care presupune de cele mai multe ori protocoale complexe. Unul dintre rolurile sistemului de operare este acela de a abstractiza masina hardware pentru programator, punand la dispozitie multiple biblioteci de functii ce pot fi apelate pentru anumite operatii frecvente, cum ar fi afisarea datelor intr-un anumit format, gasirea unui subsir intr-un sir sau diverse functii matematice.

Utilizarea unei functii presupune saltul la portiunea de cod corespunzatoare functiei, executia acestui cod, urmata de revenirea la instructiunea de dupa cea care a apelat functia, ca in figura de mai jos:

Utilizarea functiilor externe. Conventii de apel

Pentru apelul unei functii se foloseste instructiunea call.

Sintaxa:

call <nume_functie_sistem>

Semantica:

Aceasta instructiune pune adresa instructiunii urmatoare pe stiva, apoi sare la inceputul functiei apelate. Punerea pe stiva a adresei instructiunii urmatoare (adresa de revenire) se face pentru ca la finalul executiei sa se poata reveni la codul apelant.

Apelarea unei functii sistem. Parametrii

De cele mai multe ori, functiile pot primi parametri, si pot intoarce rezultate. Exista mai multe conventii pentru a face aceste lucruri, dintre care in acest laborator vom folosi conventia cdecl.

O conventie de apel nu tine de sintaxa limbajului de asamblare, ci este un ”contract” intre autorul functiei si utilizatorii acesteia, ce specifica modul de transmitere a parametrilor, respectiv de intoarcere a rezultatului.

Conventia cdecl

  • Argumentele se pun pe stiva de la dreapta la stanga
  • Rezultatul implicit este returnat de catre functia sistem in EAX
  • Registrii EAX, ECX, EDX pot fi folositi in interiorul functiei (isi pot modifica valoarea!)
  • Atentie deci, daca doriti pastrarea valorilor initiale din EAX, ECX, EDX sa salvati aceste valori (in variabile de memorie, pe stiva sau in alti registrii) inainte de apelul functiei
  • Functia nu va elibera argumentele de pe stiva. Este responsabilitatea programatorului sa faca acest lucru dupa apelul functiei
  • Exemple: printf, scanf

Functii standard din msvcrt

Afisarea pe ecran

Pentru afisarea unui text pe ecran, ce respecta un anumit format, se foloseste functia printf.

Sintaxa functiei printf in limbaj de programare de nivel inalt este:

int printf(const char * format, variabila_1, constanta_2, ...);

Functia printf respecta conventia cdecl.

Primul argument al functiei este un sir de caractere ce contine formatul afisarii, urmat de un numar de argumente (valori constante sau nume de variabile) egal cu cel specificat in cadrul formatului.

Sirul de caractere transmis in parametrul format poate contine anumite marcaje de formatare, ce incep cu caracterul ’%’, care vor fi inlocuite de valorile specificate in urmatoarele argumente, formatate corespunzator.

Specificator Ce se afiseaza Exemplu Dimensiune reprezentare valoare
c Caracter a byte
d sau i Intreg zecimal cu semn 392 dword
u Intreg zecimal fara semn 7235 dword
x Numar in hexazecimal fara semn 7fa dword
s String (sir de caractere, terminat cu 0) exemplu sir de bytes terminat in 0
 

La intrarea in corpul functiei printf, in varful stivei se afla un dublucuvant cu Adresa de Revenire, apoi imediat sub el se afla alt dublucuvant care va contine offsetul stringului de formatare - argument al functiei printf. Sub acest dublucuvant se vor afla alte dublucuvinte corespunzatoare celorlalte argumente ale functiei printf. Descriptorii de afisare %d, %i, %c, %x, %u in cazul functiei printf vor fi aplicati totdeauna unui dublucuvant din stiva (chiar si in cazul lui %c care foloseste si el un dublucuvant de pe stiva, dar din acest dublucuvant considera doar octetul cel mai putin semnificativ). Astfel, primul descriptor de afisare se va aplica celui de-al treilea dublucuvant de pe stiva (numarat fata de varful stivei). Cel de-al doilea eventual descriptor se va aplica celui de-al patrulea dublucuvant de pe stiva (numarat fata de varful stivei), cel de-al treilea eventual descriptor din stringul de formatare se va aplica celui de-al cincilea dublucuvant de pe stiva (numarat fata de varful stivei) si tot asa.

Exemple:

Afisarea unui mesaj
  • In limbaj de programare de nivel inalt:
  • 
    	printf("Ana are mere");
    	
  • Echivalentul in limbaj de asamblare:
  • 
    	segment data use32 class=data
    		mesaj  db "Ana are mere", 0  ; definim mesajul
    	segment code use32 class=code
    		; ...
    		 push dword mesaj  ; punem parametrul pe stiva
    		 call [printf]       ; apelam functia printf
    		 add esp, 4 * 1     ; eliberam parametrii de pe stiva
    		; ...
    
Afisarea unui numar natural cu semn in baza 10
  • In limbaj de programare de nivel inalt:
  • 
    	printf("%d", -17);
    	
  • Echivalentul in limbaj de asamblare:
  • 
    	segment data use32 class=data
    		format  db "%d", 0  ; definim formatul
    	segment code use32 class=code
    		; ...
    		 push dword -17  ; punem parametrii pe stiva de la dreapta la stanga
    		 push dword format  
    		 call [printf]       ; apelam functia printf
    		 add esp, 4 * 2     ; eliberam parametrii de pe stiva
    		; ...
    
Afisarea unui numar natural in baza 16
  • In limbaj de programare de nivel inalt:
  • 
    	printf("%x", 0xAB);
    	
  • Echivalentul in limbaj de asamblare:
  • 
    	segment data use32 class=data
    		format  db "%x", 0  ; definim formatul
    	segment code use32 class=code
    		; ...
    		 push dword 0xAB  ; punem parametrii pe stiva de la dreapta la stanga
    		 push dword format  
    		 call [printf]       ; apelam functia printf
    		 add esp, 4 * 2     ; eliberam parametrii de pe stiva
    		; ...
    
Afisarea unui mesaj care contine un numar natural in baza 10, stocat intr-o variabila n
  • In limbaj de programare de nivel inalt:
  • 
    	printf("Ana are %d mere", n);
    	
  • Echivalentul in limbaj de asamblare:
  • 
    	segment data use32 class=data
    		n dd 7
    		format  db "Ana are %d mere", 0  ; definim formatul
    	segment code use32 class=code
    		; ...
    		 push dword [n]  ; punem pe stiva valoarea lui n
    		 push dword format  
    		 call [printf]       ; apelam functia printf
    		 add esp, 4 * 2     ; eliberam parametrii de pe stiva
    		; ...
    
Afisarea unui mesaj care contine mai multe numere in baza 10.
  • In limbaj de programare de nivel inalt:
  • 
    	printf("Ana are %d mere si %d pere", 7, 8);
    	
  • Echivalentul in limbaj de asamblare:
  • 
    	segment data use32 class=data
    		format  db "Ana are %d mere si %d pere", 0  ; definim formatul
    	segment code use32 class=code
    		; ...
    		 push dword 8  ; punem parametrii pe stiva
    		 push dword 7 
    		 push dword format  
    		 call [printf]       ; apelam functia printf
    		 add esp, 4 * 3     ; eliberam parametrii de pe stiva
    		; ...
    
 

Citirea de la tastatura

Pentru a citi date de la tastatura se foloseste functia scanf.

Sintaxa functiei scanf in limbaj de programare de nivel inalt este:

int scanf(const char * format, adresa_variabila_1, ...);

Sintaxa acestei functii este similara cu cea a functiei printf. Diferenta majora consta in faptul ca argumentele sale nu trebuie sa fie valori constante sau continuturi de variabile, ci numai adrese in memorie, unde se vor stoca valorile citite.

Exemplu:

Citirea unui numar natural in variabila n
  • In limbaj de programare de nivel inalt:
  • 
    	scanf("%d", &n);
    		
    	 &n reprezinta adresa variabilei n in care functia scanf va completa valoarea citita de la tastatura
    	
  • Echivalentul in limbaj de asamblare:
  • 
    	segment data use32 class=data
    		n dd  0       ; definim variabila n
    		format  db "%d", 0  ; definim formatul
    	segment code use32 class=code
    		; ...
    		push dword n       ; punem parametrii pe stiva de la dreapta la stanga
    		push dword format
    		call [scanf]       ; apelam functia scanf pentru citire
    		add esp, 4 * 2     ; eliberam parametrii de pe stiva
    		; ...
    
    
    

Operatii cu fisiere text

Operatii cu fisiere text

Un fisier reprezinta o secventa de octeti. Pentru a citi dintr-un fisier sau pentru a scrie intr-un fisier, e nevoie de 3 pasi:
  1. Deschiderea fisierului, care poate consta in:
    • Deschiderea unui fisier existent
    • Crearea unui fisier nou
  2. Efectuarea operatiilor de scriere si/sau citire
  3. Inchiderea fisierului.

Deschiderea unui fisier

Pentru deschiderea unui fisier existent sau crearea unui fisier nou, se foloseste functia fopen

Sintaxa functiei

fopen in limbaj de programare de nivel inalt este:
FILE * fopen(const char* nume_fisier, const char * mod_acces)
Functia fopen respecta conventia cdecl si se gaseste in msvcrt.dll .

Argumentele functiei fopen:

Primul argument al functiei este adresa unui sir de caractere reprezentand numele fisierului: Al doilea argument este adresa unui sir de caractere, reprezentand modalitatea in care se va deschide fisierul.
Mod Semnificatie Descriere
r citire (read) -    Deschide un fisier text pentru citire. Fisierul trebuie sa existe deja pe disc.
w scriere (write) -    Daca nu exista un fisier cu acel nume, creaza fisierul si il deschide pentru scriere.
-    Daca un fisier cu acel nume exista deja, deschide acel fisier pentru scriere. Continutul initial va fi sters. Scrierea se va face de la inceputul fisierului.
a adaugare (append) -    Daca nu exista un fisier cu acel nume, creaza fisierul si il deschide pentru scriere.
-    Daca un fisier cu acel nume exista deja, deschide acel fisier pentru scriere, dar scrierea se va face la sfarsitul fisierului, in continuarea continutului existent. Continutul initial al fisierului va fi pastrat.
r+ citire si scriere fisier existent -    Deschide un fisier text pentru citire si scriere. Fisierul trebuie sa existe deja pe disc.
w+ citire si scriere -    Daca nu exista un fisier cu acel nume, creaza fisierul si il deschide pentru citire si scriere.
-    Daca un fisier cu acel nume exista deja, deschide acel fisier pentru citire si scriere. Continutul initial va fi sters. Scrierea se va face de la inceputul fisierului.
a+ citire si adaugare -    Daca nu exista un fisier cu acel nume, creaza fisierul si il deschide pentru citire si scriere.
-    Daca un fisier cu acel nume exista deja, deschide acel fisier pentru citire si scriere. Continutul initial al fisierului va fi pastrat. Citirea se va face de la inceputul fisierului. Scrierea se va face in continuarea continutului existent.

Observatii:

  • Numele unui fisier trebuie sa includa si extensia (ex: nume.txt, exemplu.asm).
  • Fisierele se vor crea sau deschide din directorul curent (in acelasi director in care se afla fisierul sursa asm). Important: pentru a putea deschide un fisier existent folosind numele acestuia, fisierul trebuie sa se afle in acelasi director cu fisierul sursa .asm, altfel acesta nu va fi gasit.
  • Operatiile de scriere nu vor reusi pentru fisiere deschise doar cu drepturi de citire (ex: ”r”). Operatiile de citire nu vor reusi pentru fisiere deschise doar cu drepturi de scriere sau adaugare (ex: ”w”, ”a”)
  • Ambele argumente ale functiei fopen reprezinta siruri de caractere care trebuie sa se termine cu valoarea 0 (asemenea formatului pentru functia printf).

Valoarea returnata de functia fopen:

Daca fisierul este deschis cu succes, functia fopen va completa in registrul EAX un identificator (descriptor de fisier) care va fi folosit in continuare pentru a lucra cu acel fisier (pentru operatii de scriere, citire, etc.). Altfel (in caz de eroare), functia fopen va completa in registrul EAX valoarea 0.

Alte observatii:

Este important sa se verifice valoarea returnata de functie in EAX (daca nu a fost eroare), inainte de a efectua alte operatii cu acel fisier. Daca in cadrul unui program se deschid mai multe fisiere diferite folosind functia fopen, fiecare valoare returnata de functiei trebuie salvata separat, deoarece reprezinta o valoare distincta prin care este identificat un fisier. Dupa finalizarea lucrului cu un fisier deschis, este important sa se si inchida acel fisier (de obicei se face la finalul programului – inainte de exit). Pentru a inchide un fisier (deschis in prealabil de functia fopen) se foloseste functia fclose.

Scrierea intr-un fisier

Pentru a scrie un text intr-un fisier se foloseste functia fprintf.

Sintaxa functiei

fprintf in limbaj de programare de nivel inalt este:
int fprintf(FILE * stream, const char * format, <variabila_1>, <constanta_2>, <...>)
Functia fprintf respecta conventia cdecl si se gaseste in msvcrt.dll . Sintaxa functiei fprintf este asemantoare cu sintaxa functiei printf (folosita pentru afisare in consola). Diferenta este faptul ca functia fprintf are ca prim parametru identificatorul fisierului in care se va scrie textul, in plus fata de parametrii functiei printf.

Argumentele functiei fprintf

Primul argument al functiei reprezinta descriptorul de fisier (identificatorul) returnat de apelul functiei fopen. Urmatorul argument al functiei este un sir de caractere ce contine formatul afisarii, urmat de un numar de argumente (valori constante sau nume de variabile) egal cu cel specificat in cadrul formatului. Asemenea functiei printf, sirul de caractere transmis in parametrul format poate contine anumite marcaje de formatare, ce incep cu caracterul ’%’, care vor fi inlocuite de valorile specificate in urmatoarele argumente, formatate corespunzator.
Specificator Ce se afiseaza Exemplu Dimensiune reprezentare valoare
c Caracter a byte
d sau i Intreg zecimal cu semn 392 dword
u Intreg zecimal fara semn 7235 dword
x Numar in hexazecimal fara semn 7fa dword
s String (sir de caractere, terminat cu 0) exemplu sir de bytes terminat in 0

Citirea dintr-un fisier

Pentru a citi un text dintr-un fisier se foloseste functia fread.

Sintaxa functiei

fread in limbaj de programare de nivel inalt este:
int fread(void * str, int size, int count, FILE * stream)
Functia fread respecta conventia cdecl si se gaseste in msvcrt.dll .

Argumentele functiei fread

Primul argument al functiei fread reprezinta adresa unui sir de elemente in care se vor completa datele citite din fisier. Al doilea argument reprezinta dimensiunea unui element care va fi citit din fisier. Al treilea argument reprezinta numarul maxim de elemente care se vor citi din fisier. Ultimul argument al functiei reprezinta descriptorul de fisier (identificatorul) returnat de apelul functiei fopen. In cazul citirii fisierelor text, primul argument al functiei fread este un sir de bytes si al doilea argument este 1 (=dimensiunea unui byte). Al treilea argument este dimensiunea sirului de bytes (numarul de elemente).

Valoarea returnata de functia fread:

Functia fread va completa in registrul EAX numarul de elemente citite. Daca acest numar este mai mic decat valoarea argumentului count, atunci fie apelul functiei fread a intampinat o eroare la citire, fie s-a ajuns la finalul fisierului.

Observatii:

Fisierele text pot fi avea dimensiuni prea mari pentru putea citi continutul acestora cu un singur apel al functiei fread. In acest caz este nevoie de apeluri repetate ale functiei fread, pana cand intreg continutul fisierului este citit. In sectiunea „Exemple” vom prezenta un program care exemplifica acest scenariu. Pentru a verifica daca s-a ajuns la finalul fisierului cu operatia de citire se poate verifica daca valoarea returnata de fread este 0.

Inchiderea unui fisier deschis

Dupa finalizarea lucrului cu un fisier deschis, acesta trebuie inchis. Acest pas nu trebuie sa lipseasca dintr-un program care a deschis fisiere. Pentru inchiderea unui fisier se foloseste functia fclose.

Sintaxa functiei

fclose in limbaj de programare de nivel inalt este:
int fclose(FILE * descriptor)
Functia fclose respecta conventia cdecl si se gaseste in msvcrt.dll .

Argumentul functiei fclose

Argumentul functiei fclose este descriptorul de fisier (identificatorul) returnal de apelul functiei fopen.