Sunteți pe pagina 1din 380

Programarea procesoarelor IA-32

n limbaj de asamblare

tefan Gabriel origa

Iulian Bdescu

UNIVERSITATEA POLITEHNICA BUCURETI

PROGRAMAREA PROCESOARELOR IA-32 N


LIMBAJ DE ASAMBLARE

PREFA
Limbajul de asamblare reprezint o metod de codificare simbolic, relativ
uor de citit i de interpretat de ctre om, a instruciunilor n format binar executate
de procesor. Spre deosebire de alte limbaje de programare, limbajul de asamblare
nu reprezint un limbaj de sine stttor, ci, mai degrab, o categorie de limbaje.
Fiecare familie de procesoare are propriul limbaj n asamblare. Din aceast relaie
de cauzalitate rezult c programatorul, pe lng noiunile specifice limbajului,
trebuie s dein i cunotine minime legate de structura intern a procesorului.
Devine evident c programarea n limbaj de asamblare este mult mai dificil dect
programarea ntr-un limbaj de nivel nalt. De fapt, limbajul de asamblare trebuie
utilizat numai atunci cnd este absolut necesar.
Deoarece asamblarea este o etap intermediar a procesului de compilare,
instrumentele folosite la translatarea codului scris n limbaj de nivel nalt n limbaj
main sunt capabile s gestioneze i programe hibride, n care codul limbajului de
nivel nalt conine secvene scrise n limbaj de asamblare. Altfel spus, compilatorul
accept introducerea de linii scrise n limbaj de asamblare direct n textul surs al
limbajului de nivel nalt, iar editorul de legturi poate combina module obiect
generate din surse scrise n limbaj de nivel nalt cu module obiect obinute din
surse scrise n limbaj de asamblare. Aceste construcii hibride apar din cteva
considerente, dintre care amintim:
optimizri atunci cnd sunt necesare secvene critice sub aspectul
timpului de execuie i consumului de memorie;
acces la instruciuni specifice procesorului;
n prezent, chiar i n aceste situaii, eficiena compilatoarelor a crescut
pn la nivelul n care acestea concureaz cu orice programator n asamblare, cu
excepia unuia foarte bun, iar evoluiile tehnologice fac ca avantajul codului main
optimizat prin instruciuni n asamblare s fie minim. Pe de alt parte, odat cu
rspndirea unor platforme relativ srace n resurse de procesare i memorie, cum
ar fi telefoanele mobile inteligente sau microcontrolere, se preconizeaz o cretere
a cererii de specialiti capabili s foloseasc ct mai eficient resursele existente.
Totodat, sunt i situaii n care programarea n limbaj de asamblare nu poate fi
evitat. De exemplu, anumite componente ale sistemului de operare au restricii
stricte n ce privete performana i consumul de resurse. Acestea nu se pot realiza
dect prin utilizarea ct mai eficient a instruciunilor i caracteristicilor
procesorului. n general, programatorii de sistem au nevoie de cunotine avansate
de programare n limbaj de asamblare.
n cazul programatorilor de aplicaii, principalele motive pentru care se
recomand experiena programrii n limbaj de asamblare const n familiarizarea
cu modul de organizare a programelor n memorie, cu principiile de funcionare a
diverselor componente hardware sau cu rolul sistemului de operare. Toate ajut
programatorul de aplicaii s scrie programe mai eficiente. De asemenea, depanarea
unui program de nivel nalt poate depi nivelul textului surs, ajungndu-se la

depanarea codului obiect, proces care necesit cunoaterea limbajului de


asamblare. Evident, dac se dorete ca o aplicaie s profite ct mai mult de
beneficiile limbajului de asamblare (cod compact i rapid, consum de resurse
minim), aceasta poate fi scris complet n limbaj de asamblare. Un astfel de caz
este aplicaia antivirus NOD32.
Cartea de fa abordeaz limbajul de asamblare corespunztor familiei de
procesoare Intel de 32 de bii, de la 80386 pn la Pentium 4. Indiferent de
particularitile limbajului de asamblare specific arhitecturilor Intel, noiunile de
baz i principiile generale se aplic i altor categorii de procesoare i chiar
microcontrolerelor. Gradul de complexitate al informaiilor crete gradual, pe
parcursul desfurrii capitolelor. Publicul int este reprezentat de studenii de
anul II de la specializarea Telecomenzi i electronic n transporturi din cadrul
Facultii Transporturi, dar i de studeni de la alte faculti, de exemplu, Facultatea
de Automatic i Calculatoare sau Electronic i Telecomunicaii, care au n
program cursuri de Arhitectura microprocesoarelor sau Calculatoare i sisteme de
operare. Lucrarea este dedicat celor care iau contact pentru prima dat cu limbajul
de asamblare i, chiar dac publicul int a urmat cel puin un curs introductiv de
programare, nu se fac presupuneri cu privire la cunotinele acumulate anterior.
Conceptele sunt prezentate simplu i concis, natura intrinsec a domeniului
necesit oricum un grad ridicat de concentrare i exerciiu. Programele sunt la fel
de simple, orientate ctre ilustrarea conceptelor. n final, pentru a fora cititorul s
editeze programele manual, am ales ca volumul s nu fie acompaniat de un CD. Se
tie c tentaia de a copia codul cu ajutorul mouse-ului e mare. Cititorul trebuie s
contientizeze faptul c obiectivul su este s i nsueasc informaiile i
conceptele prezente n acele programe, nu s le ruleze pur i simplu. Autorii
recomand parcurgerea secvenial a crii, ntotdeauna cu calculatorul n fa.
Aceast carte nu se citete, se execut. Conceptele noi sunt prezentate pe baza celor
dinainte, iar capitolele sunt scrise astfel nct s conduc ct mai repede la exemple
practice. Cu o atitudine adecvat, la finalul crii, n cel mai pesimist scenariu,
cititorul va cunoate conceptele generale de dezvoltare a programelor n asamblare,
funciile sistemului de operare i organizarea calculatorului din perspectiva unui
programator.
Considerm c platforma hardware i software cea mai indicat pentru
nsuirea conceptelor teoretice este cea real. De aceea, toate uneltele de
programare discutate n aceast carte sunt disponibile pe orice distribuie Linux.
Linux ofer un excelent mediu de studiu i dezvoltare. n plus, contactul cu Linux
reprezint o oportunitate n sine. Executabilele sunt generate i rulate chiar de
procesorul calculatorului (cu excepia cazului n care alegei s folosii o main
virtual). Cerina obligatorie este s fac parte din familia Intel de 32 sau 64 de bii.
Deoarece procesoarele moderne de 64 de bii sunt compatibile napoi, toate
informaiile prezentate pentru arhitecturile de 32 de bii rmn valabile.

Mediul de dezvoltare
n procesul de dezvoltare a programelor, majoritatea programatorilor
folosesc un mediu integrat de dezvoltare (IDE - Integrated Development
Environment). Acesta pune la dispoziie toate uneltele necesare generrii
executabilului din codul surs, dar, totodat, ascunde detaliile acestui proces. n
aceast carte folosim componentele (editor de text, compilator, asamblor, editor de
legturi, depanator) individual, astfel nct rolul fiecruia s poat fi observat
direct. Sistemul de operare este Ubuntu, orice variant ntre 10.04 i 13.04.
Organizarea capitolelor
Primul capitol prezint sistemele de numeraie i modul de reprezentare a
caracterelor alfanumerice n sistemele de calcul. Sunt oferite numai informaii strict
necesare. Scopul este s ne nsuim rapid un set de cunotine minim pe baza cruia
s putem asimila, prin exemple practice, conceptele ulterioare.
Al doilea capitol prezint arhitectura sistemelor de calcul, cu cele dou
faete ale sale, hardware i software. Subiectul este abordat din perspectiva
programatorului. Ne concentrm asupra structurii interne a procesoarelor Intel de
32 de bii.
Al treilea capitol prezint limbajul de asamblare n contextul interaciunii
utilizator - calculator i al poziei sale n ierarhia limbajelor de programare.
Capitolul poate fi privit ca o ncercare de definire a limbajului de asamblare prin
gen i diferen specific, unde genul este reprezentat de clasa limbajelor de
programare, iar diferena specific de proprietile caracteristice. Din acest capitol
se desprinde faptul c limbajul de asamblare este o etap parcurs de compilatoare
n procesul de translatare a codului surs n cod main. Aadar, fie c dorim s-l
folosim sau nu, el este oricum utilizat de compilatoare n procesul de generare a
codului obiect.
Al patrulea capitol prezint procesul de dezvoltare a programelor n limbaj
de asamblare i uneltele utilizate n etapele acestuia. Sunt prezentate numai
informaii eseniale legate de limbajul de asamblare. Toate capitolele urmtoare vor
face referire la acestea.
Al cincilea capitol este dedicat conceptului central al programrii n orice
limbaj, cu att mai mult al programrii n limbaj de asamblare: organizarea i
adresarea memoriei principale.
Al aselea capitol prezint formatul instruciunilor main. Pe lng aceste
informaii, rolul acestui capitol este s familiarizeze cititorul cu modul de
prezentare a informaiilor n documentaia oficial Intel1.
Al aptelea i al optulea capitol acoper toate instruciunile dedicate
1

The Intel 64 and IA-32 Architectures Software Developers Manual, Volumes


2A & 2B: Instruction Set Reference

operaiilor cu numere ntregi, respectiv cu numere reale.


Ultimele trei capitole acoper noiuni mai avansate, precum definirea
funciilor i interaciunea limbajului de asamblare cu sistemul de operare i
limbajul de nivel nalt C.

Cuprins
1.! REPREZENTAREA INFORMAIEI N SISTEMELE DE CALCUL14!
1.1.! Sisteme de numeraie ......................................................................14!
1.1.1.! Sistemul zecimal......................................................................15!
1.1.2.! Sistemul binar ..........................................................................15!
1.1.3.! Sistemul hexazecimal ..............................................................16!
1.2.! Operaii de conversie ......................................................................16!
1.2.1.! Conversia numerelor din zecimal n binar ..............................16!
1.2.2.! Conversia numerelor din zecimal n hexazecimal ...................17!
1.2.3.! Conversia numerelor din hexazecimal n binar .......................17!
1.2.4.! Conversia numerelor din binar n hexazecimal .......................18!
1.2.5.! Conversia numerelor reale.......................................................19!
1.3.! Reprezentarea caracterelor alfanumerice .......................................20!
1.4.! Exerciii ..........................................................................................22!
2.! ARHITECTURA CALCULATOARELOR..........................................24!
2.1.! Structura unui sistem de calcul .......................................................24!
2.1.1.! Arhitectura von Neumann .......................................................24!
2.1.2.! Modelul bazat pe magistral....................................................26!
2.1.1.! Magistrala de sistem ................................................................27!
2.1.2.! Unitatea de intrare/ieire .........................................................28!
2.1.3.! Memoria ..................................................................................29!
2.2.! Arhitectura IA-32 ...........................................................................30!
2.2.1.! Arhitectur i microarhitectur ................................................31!
2.2.2.! Structura de principiu a procesorului ......................................33!
2.2.3.! Funcionarea procesorului .......................................................37!
2.2.4.! Registrele procesorului ............................................................38!
2.2.5.! ntreruperile .............................................................................42!
2.3.! Exerciii ..........................................................................................43!
3.! LIMBAJUL DE ASAMBLARE ...........................................................44!
3.1.! Tipuri de limbaje de programare ....................................................46!
3.1.1.! Limbajul main ......................................................................46!
3.1.2.! Limbajul de asamblare ............................................................48!
3.1.3.! Limbaje de nivel nalt ..............................................................48!
3.2.! Procesul de compilare.....................................................................51!
3.2.1.! Preprocesarea...........................................................................53!
3.2.2.! Compilarea ..............................................................................53!

3.2.3.! Asamblarea ..............................................................................55!


3.2.4.! Editarea legturilor ..................................................................57!
3.2.5.! Formatul fiierelor executabile ................................................57!
3.3.! Avantajele limbajului de asamblare ...............................................58!
3.4.! Exerciii ..........................................................................................59!
4.! DEZVOLTAREA PROGRAMELOR N LIMBAJ DE ASAMBLARE
60!
4.1.! Sintaxa limbajului de asamblare .....................................................60!
4.2.! Structura programului .....................................................................61!
4.3.! Structura procesului ........................................................................64!
4.3.1.! Memoria virtual n Linux ......................................................65!
4.4.! Tipuri de date ..................................................................................67!
4.4.1.! Date iniializate........................................................................67!
4.4.2.! Date neiniializate ....................................................................74!
4.5.! Procesul de dezvoltare al programelor ...........................................75!
4.5.1.! Editarea textului ......................................................................75!
4.5.2.! Asamblarea ..............................................................................81!
4.5.3.! Editarea legturilor ..................................................................85!
4.5.4.! Automatizarea sarcinilor cu GNU Make .................................86!
4.5.5.! Execuia programului ..............................................................89!
4.5.6.! Depanarea fiierului executabil ...............................................90!
4.6.! ntrebri i exerciii.......................................................................100!
5.! MEMORIA ..........................................................................................101!
5.1.! Declararea datelor n bloc .............................................................101!
5.2.! Adresarea memoriei n modul protejat .........................................103!
5.2.1.! Adresare imediat i adresare la registre ...............................104!
5.3.! Optimizarea accesului la memorie ...............................................117!
6.! ARHITECTURA SETULUI DE INSTRUCIUNI ............................122!
6.1.! Simboluri cheie .........................................................................122!
6.2.! Codificarea instruciunilor ............................................................124!
6.2.1.! Spaiul codurilor operaionale ...............................................126!
6.2.2.! Octetul ModR/M ...................................................................128!
6.2.3.! Spaiul codurilor 386 (0F + ...) ..............................................129!
6.2.4.! Prefix de dimensiune operand ...............................................130!
6.3.! Codificarea adreselor de memorie ................................................131!
6.3.1.! Octetul SIB ............................................................................132!

6.4.! Formatul instruciunilor ................................................................133!


6.4.1.! Prefixele de instruciune ........................................................133!
6.4.2.! Modurile de adresare prin octetul ModR/M ..........................134!
6.4.3.! Modurile de adresare prin octetul SIB ..................................137!
6.5.! Studiu de caz.................................................................................139!
7.! OPERAII CU NUMERE NTREGI .................................................145!
7.1.! Reprezentarea numerelor cu semn................................................145!
7.1.1.! Reprezentarea cu bit de semn i magnitudine .......................146!
7.1.2.! Reprezentarea n complement fa de unu.............................146!
7.1.3.! Reprezentarea n complement fa de doi .............................147!
7.2.! Extinderea ntregilor .....................................................................151!
7.3.! Indicatori de stare .........................................................................153!
7.3.1.! Indicatorul de zero .................................................................153!
7.3.2.! Indicatorul de transport .........................................................154!
7.3.3.! Indicatorul de depire ..........................................................155!
7.3.4.! Indicatorul de semn ...............................................................156!
7.3.5.! Indicatorul de transport la jumtate .......................................156!
7.3.6.! Indicatorul de paritate ............................................................157!
7.4.! Instruciuni de transfer condiionat ...............................................158!
7.5.! Operaii aritmetice ........................................................................160!
7.5.1.! Instruciuni de adunare ..........................................................160!
7.5.2.! Instruciuni de scdere ...........................................................165!
7.5.3.! Instruciuni de comparare ......................................................170!
7.5.4.! Incrementare i decrementare ................................................170!
7.5.5.! Instruciuni de nmulire ........................................................171!
7.5.6.! Instruciuni de mprire ........................................................172!
7.6.! Instruciuni de interschimbare a datelor .......................................173!
7.7.! Instruciuni de prelucrare la nivel de bit .......................................178!
7.7.1.! Instruciuni logice ..................................................................178!
7.7.2.! Instruciuni de deplasare ........................................................181!
7.7.3.! Instruciuni de rotaie.............................................................185!
7.7.4.! Instruciuni de testare i modificare a unui bit ......................185!
7.7.5.! Instruciuni de scanare pe bit .................................................186!
7.7.6.! Instruciuni de setare condiionat .........................................186!
7.8.! Instruciuni de transfer al controlului ...........................................187!
7.8.1.! Instruciuni de salt necondiionat ..........................................188!
7.8.2.! Instruciuni de salt condiionat ..............................................189!
7.8.3.! Instruciuni de ciclare ............................................................191!
7.9.! Procesarea irurilor .......................................................................193!

7.9.1.! Instruciuni de transfer...........................................................193!


7.9.2.! Instruciuni de transfer la/de la acumulator ...........................197!
7.9.3.! Instruciuni de comparare ......................................................200!
7.9.4.! Instruciuni de parcurgere ......................................................201!
7.10.! Operaii cu stiva ..........................................................................202!
7.10.1.! Introducerea i extragerea datelor .......................................203!
7.10.2.! Introducerea i extragerea registrelor ..................................205!
7.10.3.! Exemple de lucru cu stiva ...................................................206!
7.11.! Exerciii ......................................................................................208!
8.! OPERAII CU NUMERE N VIRGUL MOBIL .........................209!
8.1.! Reprezentarea numerelor reale .....................................................209!
8.1.1.! Formatul n virgul mobil ....................................................209!
8.1.2.! Standardul IEEE 754 .............................................................214!
8.1.3.! Valori n virgul mobil specifice IA-32 ..............................218!
8.2.! Arhitectura unitii n virgul mobil ...........................................219!
8.2.1.! Registre de date .....................................................................219!
8.2.2.! Registre de control i stare ....................................................220!
8.3.! Instruciuni n virgul mobil .......................................................224!
8.3.1.! Transferul datelor n virgul mobil ......................................224!
8.3.2.! Operaii aritmetice n virgul mobil ....................................230!
8.3.3.! Instruciuni transcedentale .....................................................235!
8.3.4.! Instruciuni de comparare ......................................................236!
8.3.5.! Instruciuni FPU de transfer condiionat ...............................240!
8.4.! Exerciii ........................................................................................244!
9.! FUNCII .............................................................................................247!
9.1.! Modularizarea programelor ..........................................................247!
9.2.! Apelarea funciilor ........................................................................248!
9.3.! Definirea funciilor .......................................................................249!
9.4.! Transferul controlului ...................................................................250!
9.5.! Metode de transfer al parametrilor ...............................................251!
9.5.1.! Prin registre ...........................................................................251!
9.5.2.! Prin variabile globale.............................................................253!
9.5.3.! Prin stiv ................................................................................254!
9.5.4.! Structura funciei ...................................................................262!
9.6.! Mecanisme de transfer al parametrilor .........................................266!
9.6.1.! Transfer prin valoare .............................................................266!
9.6.2.! Transfer prin referin ...........................................................269!

9.7.! Conservarea strii registrelor ........................................................271!


9.8.! Scrierea funciilor n fiiere separate ............................................271!
9.9.! Exerciii ........................................................................................275!
10.! INTERFAA CU SISTEMUL DE OPERARE ................................278!
10.1.! ntreruperi software ....................................................................278!
10.2.! Formatul apelurilor de sistem .....................................................281!
10.2.1.! Alocarea static a bufferelor................................................288!
10.2.2.! Macroinstruciuni ................................................................289!
10.2.3.! Funcii de lucru cu transferuri de intrare/ieire ...................296!
10.2.4.! Operaii cu fiiere ................................................................303!
10.2.5.! Argumente n linia de comand...........................................307!
10.3.! Operaii aritmetice n reprezentri ASCII i BCD .....................311!
10.3.1.! Formatul BCD .....................................................................312!
10.3.2.! Erori i corecii n ASCII.....................................................312!
10.3.3.! Erori i corecii n BCD compactat .....................................320!
10.3.4.! Conversia ASCII binar ........................................................322!
10.3.5.! Conversia caracterelor ASCII n binar ................................324!
10.3.6.! Conversia numerelor din binar n ASCII ............................331!
10.4.! Biblioteci de funcii ....................................................................335!
10.4.1.! Crearea bibliotecilor statice .................................................335!
10.4.2.! Crearea bibliotecilor partajate .............................................337!
10.4.3.! Procesul de generare a bibliotecilor partajate......................344!
10.4.4.! Instalarea i utilizarea bibliotecilor partajate.......................346!
10.5.! Apelul funciilor de sistem prin biblioteca standard C ...............348!
10.5.1.! Editarea legturilor cu funciile C .......................................352!
11.! INTERFAA CU LIMBAJELE DE NIVEL NALT .......................355!
11.1.! Apelarea modulelor n asamblare din C ....................................356!
11.1.1.! Structura funciei n asamblare ............................................357!
11.1.2.! Cadrul de stiv .....................................................................359!
11.1.3.! Compilarea modulelor .........................................................361!
11.2.! Linii de asamblare n codul surs C............................................364!
11.2.1.! Sintaxa AT&T .....................................................................364!
11.2.2.! Formatul de baz .................................................................368!
11.2.3.! Formatul extins ....................................................................373!
Bibliografie .................................................................................................379!

1. REPREZENTAREA INFORMAIEI N
SISTEMELE DE CALCUL

Oamenii comunic prin intermediul unui numr apreciabil de simboluri:


cifre, litere, semne de punctuaie, operatori aritmetici, alte semne speciale.
Calculatorul nu este capabil s utilizeze dect dou simboluri (0 i 1). Pentru a
reui s comunice cu un calculator, omul trebuie s converteasc simbolurile i
comenzile sale ntr-o form pe care calculatorul s o poat nelege.
Capitolul ncepe cu studiul diferitelor sisteme de numeraie utilizate n
interaciunea cu calculatorul. Cnd se lucreaz cu mai multe sisteme de numeraie,
adeseori este nevoie s convertim un numr dintr-o reprezentare n alta.
Prezentm detalii cu privire la aceste conversii. Apoi ne concentrm asupra
reprezentrii caracterelor alfanumerice. n general, prin caractere alfanumerice se
neleg toate caracterele care pot fi introduse de la tastatura unui calculator.
ncheiem cu un set de exerciii destinat s mijloceasc reinerea
conceptelor i procedeelor utilizate de-a lungul capitolului.

1.1. Sisteme de numeraie


n mare, sistemul de calcul poate fi considerat o sum de circuite
electronice. ntr-un circuit electronic, prezena unui anumit voltaj poate indica 1, n
timp ce lipsa acestuia, n acelai punct, poate indica 0. Raionamentul poate fi
extins la prezena sau lipsa unui curent electric, a unei capaciti, etc.. Oricare ar fi
suportul electronic, putem spune c sistemele de calcul stocheaz informaia sub
forma unor iruri de 0 i 1. De exemplu:
0101001101101001011011010111000001101100011101010010000
0011000110110000100100000010000100111010101101110011000
01001000000111101001101001011101010110000100101110
Sistemul de numeraie care utilizeaz numai aceste dou simboluri (digii,
cifre) poart numele de sistem binar. Numrul de simboluri constituie baza
sistemului de numeraie. Calculatoarele stocheaz toat informaia n baza 2 limbajul nativ al mainilor de calcul. Pe de alt parte, pentru noi, fiine dotate cu

zece degete i obinuite cu sistemul zecimal, operarea n reprezentare binar este


incomod.

1.1.1. Sistemul zecimal


Numerele n baza 10 sunt reprezentate folosind zece simboluri: 0, 1, 2, 3, 4,
5, 6, 7, 8, 9. Fiecare digit al numrului zecimal are asociat o putere a lui 10, n
funcie de poziia sa n irul de cifre al reprezentrii (astfel de sisteme de numeraie
se mai numesc i poziionale). Poziia cifrei n numr este evaluat de la dreapta la
stnga, ncepnd cu zero.
369!" = 3 10! + 6 10! + 9 10!

1.1.2. Sistemul binar


Numerele n baza 2 sunt compuse din doi digii: 0 i 1. Fiecare digit al unui
numr, n funcie de poziia sa, are asociat o putere a lui 2.
11010! = 1 2! + 1 2! + 0 2! + 1 2! + 0 2!
= 16 + 8 + 2
= 26!!
Exemplul ilustreaz i conversia unui numr din binar n zecimal.
Un singur digit binar se numete bit (binary digit) i se prescurteaz cu
litera b. Pentru a lucra mai uor cu numerele binare, putem grupa mai muli bii
mpreun:
un ir de 4 bii formeaz un nibble,
un ir de 8 bii un byte sau octet,
un ir de 16 bii formeaz un word sau un cuvnt,
32 de bii un double word sau un dublu cuvnt,
64 de bii un quad word (cuvnd cvadruplu),
80 de bii un ten byte.
irul binar de la nceputul capitolului are un numr de 20 de octei. Dac l
mprim n octei i convertim fiecare octet n zecimal obinem irul:
83
66

105
117

109
110

112
97

108
32

117
122

32
105

99
117

9
97

32
46

Acelai ir de bii, structurat n grupuri de cuvinte (16 bii) reprezentate


zecimal, devine:

21329
17013

28016
28257

27765
8314

8291
26997

24864
24878

n grupuri de dublu cuvinte:


1399418224
1851859066

1819615331
1769300270

1629504117

Cu ct irurile de bii devin mai lungi, cu att ne este mai greu s le


convertim n zecimal. De aceea, folosim ca intermediar sistemul hexazecimal.

1.1.3. Sistemul hexazecimal


n sistemul de numeraie hexazecimal se utilizeaz 16 simboluri: 0, 1, 2, 3,
4, 5, 6, 7, 8, 9, A, B, C, D, E, F. Semnificaia zecimal a simbolurilor A, B, C, D,
E, F este, n ordine, 10, 11, 12, 13, 14, 15.
Similar celorlalte sisteme de numeraie, fiecare digit al numrului
hexazecimal are asociat o putere a lui 16, n funcie de poziie:
3AE!" = 3 16! + A 16! + E 16!
!!!!!!!!!!!!!= 768 + 10 16 + 14 1
!!!!!!!!!= 768 + 160 + 14
= 942!"
Exemplul ilustreaz i conversia unui numr din hexazecimal n zecimal.
Este acelai algoritm de conversie folosit anterior la trecerea din binar n zecimal.
S-a schimbat numai baza.

1.2. Operaii de conversie


Am vzut cum putem transforma un numr din baza 2 n baza 10 sau din
baza 16 n baza 10. n continuare vom demonstra alte conversii utile.

1.2.1. Conversia numerelor din zecimal n binar


Pentru a converti un numr zecimal n binar, nu trebuie dect s-l mprim
succesiv la 2 pn obinem ctul 0, iar grupul resturilor obinute, scrise de la stnga
la dreapta, ncepnd cu ultimul, formeaz reprezentarea acestuia n baza 2.
Exemplu:

135
67
33
16
8
4
2
1

:
:
:
:
:
:
:
:

2
2
2
2
2
2
2
2

=
=
=
=
=
=
=
=

67
33
16
8
4
2
1
0

rest
rest
rest
rest
rest
rest
rest
rest

1
1
1
0
0
0
0
1

=>
=>
=>
=>
=>
=>
=>
=>

d! =
d! =
d! =
d! =
d! =
d! =
d! =
d! =

1
1
1
0
0
0
0
1

Grupnd resturile n ordinea invers obinerii lor, rezult:


135!" = d! d! d! d! d! d! d! d! = 10000111! .
Verificm:
10000111! = 1 2! + 1 2! + 1 2! + 1 = 128 + 4 + 2 + 1 = 135!"

1.2.2. Conversia numerelor din zecimal n hexazecimal


Transformm numrul zecimal 1345 n hexazecimal. Algoritmul de trecere
din zecimal n hexazecimal este identic cu cel folosit la conversia din zecimal n
binar, dar mprim la noua baz, 16.
1345 : 16 = 84 rest 1 => d! = 1
84 : 16 = 5 rest 4 => d! = 4
5 : 16 = 0 rest 5 => d! = 5
n concluzie, 1345!" = d! d! d! = 541!"
Verificm:
541!" = 5 16! + 4 16 + 1 = 5 256 + 64 + 1 = 1280 + 65 = 1345!"

1.2.3. Conversia numerelor din hexazecimal n binar


Reprezentarea hexazecimal este folosit de obicei ca notaie prescurtat
pentru anumite grupuri de bii. Tabelul 1.1 ne ajut s nelegem mecanismul prin
care se convertete un numr hexazecimal n binar.

Tabelul 1.1 Reprezentarea numerelor n diferite sisteme de numeraie

Zecimal
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Binar
0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1101
1110
1111

Hexazecimal
0
1
2
3
4
5
6
7
8
9
A
B
C
D
E
F

Deoarece sistemul hexazecimal are 16 digii, fiecare cifr hexazecimal


poate specifica un grup unic de patru bii.
Din calculele precedente a reieit c 135!" este 87 n hexazecimal i
10000111 n binar. Aadar, putem deduce c reprezentarea binar a numrului
87!" este 10000111. Din tabel observm c reprezentarea binar a lui 8!" este
1000, iar reprezentarea binar a lui 7!" este 0111. Cu alte cuvinte, 10000111
reprezint alturarea echivalentului binar al cifrelor hexazecimale 8 i 7.
n concluzie, pentru a converti orice numr hexazecimal n binar, nu
trebuie dect s identificm reprezentarea binar a numerelor hexazecimale.
De exemplu, 1A3B!" :
1!" = 0001!
A!" = 1010!
3!" = 0011!
B!" = 1011!
rezult 1A3B!" = 0001101000111011!

1.2.4. Conversia numerelor din binar n hexazecimal


Pentru a converti un numr din binar n hexazecimal trebuie numai s
inversm procesul. Grupm biii numrului binar cte patru, de la dreapta la stnga,
dac este nevoie completm cu 0 biii ultimului grup, i identificm echivalentul n
hexazecimal pentru fiecare nibble.
De exemplu, numrul binar 10010001011010 poate fi grupat de la dreapta

la stnga astfel:
10 0100 0101 1010.
Completm la stnga cu doi de zero,
0010 0100 0101 1010,
i identificm pentru fiecare nibble echivalentul n hexazecimal. Rezultatul este
245A!" .
Aplicm aceast regul pe irul de bii dat la nceputul capitolului. mprit
n octei, obinem:
53
42

69
75

6d
6e

70
61

6c
20

75
7a

20
69

63
75

61
61

20
2e

mprit n cuvinte (unirea doi cte doi a octeilor de mai sus):


5369
612e

6d70

6c75

2063

6120

4275

6e61

207a

6975

mprit n dublu cuvinte (unirea dou cte dou a cuvintelor de mai sus):
53696d70

6c752063

61204275

6e61207a

6975612e

Din ultimele conversii deducem avantajul conversiei din binar n


hexazecimal fa de conversia din binar n zecimal. Reprezentarea n hexazecimal
este mult mai uor de obinut. n plus, reprezentarea hexazecimal a grupurilor de
numere binare este transparent: putem forma cuvinte prin alturarea octeilor,
dublu cuvinte prin alturarea cuvintelor, .a.m.d.. Aceste avantaje poziioneaz
sistemul hexazecimal ca excelent intermediar ntre sistemul binar, utilizat de
calculator, i sistemul zecimal, nativ utilizatorului uman. De aici reiese un concept
foarte important sistemul hexazecimal este modalitatea cea mai convenabil prin
care omul poate reprezenta iruri de bii. Un numr nu este intrinsec binar, zecimal
sau hexazecimal. O valoare poate fi exprimat la fel de precis n toate cele trei
sisteme de numeraie. Din acest punct de vedere, ea poate fi exprimat la fel de
precis n orice sistem de numeraie.

1.2.5. Conversia numerelor reale


Conversia unui numr cu parte ntreag i parte fracionar se face prin
conversia separat a celor dou pri. Conversia numrului subunitar (partea
fracionar a unui numr) din baza 10 ntr-o baz oarecare se face prin nmulirea
succesiv a prilor fracionare cu baza respectiv, pn cnd se ajunge la zero, la

perioad, sau se depete capacitatea de reprezentare (se obin cifre suficiente dei
algoritmul nu s-a finalizat). Cifrele care depesc partea fracionar la fiecare
nmulire formeaz numrul n baza ctre care se face conversia.
Exemplul care urmeaz convertete numrul 0.57 n binar i hexazecimal.
Cum partea ntreag este deja 0, convertim numai partea fracionar.
0,57
0,14
0,28
0,56
0,12
0,24
0,48
0,96
0,92
0,84
0,68
0,36

*
*
*
*
*
*
*
*
*
*
*
*

2
2
2
2
2
2
2
2
2
2
2
2

=
=
=
=
=
=
=
=
=
=
=
=

1.14
0.28
0.56
1.12
0.24
0.48
0.96
1.92
1.84
1.68
1.36
0.72

=>
=>
=>
=>
=>
=>
=>
=>
=>
=>
=>
=>

d! = 1
d! = 0
d! = 0
d! = 1
d! = 0
d! = 0
d! = 0
d! = 1
d! = 1
d! = 1
d!" = 1
d!! = 0

Aadar,
0,57!" = 0, d! d! d! d! d! d! d! d! d! d! d!" d!! = 0,100100011110! = 0,91E!"

1.3. Reprezentarea caracterelor alfanumerice


Deoarece calculatorul poate reprezenta informaia numai sub form de
iruri de 0 i 1, toate simbolurile alfanumerice (literele mari i mici ale alfabetului,
cifrele de la 0 la 9, semnele de punctuaie, operatori aritmetici i de relaie, alte
caractere speciale) trebuie traduse n format binar. Reprezentarea informaiei
conform unui anumit format se numete codificare. Rezultatul codificrii este un
cod - un set de simboluri elementare mpreun cu o serie de reguli care specific
modul de formare al acestor simboluri.
Un astfel de cod, bazat pe alfabetul englez, este ASCII (American Standard
Code for Information Interchange). ASCII este definit de Institutul American de
Standarde i recomandat de ISO (International Standard Organization) ca standard
actual de codificare alfanumeric. Codul ISO pentru ASCII este ISO 8859. Iniial,
caracterele ASCII au fost codificate pe 7 bii, ceea ce nsemna un numr maxim de
128 (2! ) de simboluri. Ulterior, a fost extins de IBM la 8 bii, rezultnd 256 de
simboluri (codul fiecrui simbol este reprezentarea n binar a numrului su de
ordine). Chiar extins la 8 bii, multe limbi conin simboluri care nu pot fi
condensate n 256 de caractere. De aceea, au fost create variante care conin
caractere i simboluri regionale. De exemplu, tabela ASCII cunoscut sub numele

de ISO 8859-1 cuprinde caractere pentru limbile indo-europene apusene (America


de Nord, Europa Occidental, Australia i Africa). Pentru limba romn se
utilizeaz tabela ISO 8859-2, destinat limbilor indo-europene rsritene, sau ISO
8859-16, zona est-european.
Tabelul 1.2 Codul ASCII
Dec Hex Char
0 0 NULL (null)
1 1 SOH (start of heading)
2 2 STX (start of text)
3 3 ETX (end of text)
4 4 EOT (end of transmission)
5 5 ENQ (enquiry)
6 6 ACK (acknowledge)
7 7 BEL (bell)
8 8 BS (backspace)
9 9 TAB (horizontal tab)
10 A LF (NL line feed, new line)
11 B VT (vertical tab)
12 C FF (NP form feed, new page)
13 D CR (carriage return)
14 E SO (shift out)
15 F SI (shift in)
16 10 DLE (data link escape)
17 11 DC1 (device control 1)
18 12 DC2 (device control 2)
19 13 DC3 (device control 3)
20 14 DC4 (device control 4)
21 15 NAK (negative acknowledge)
22 16 SYN (synchronous idle)
23 17 ETB (end of trans. block)
24 18 CAN (cancel)
25 19 EM (end of medium)
26 1A SUB (substitute)
27 1B ESC (escape)
28 1C FS (file separator)
29 1D GS (group separator)
30 1E RS (record separator)
31 1F US (unit separator)

Dec Hex Char


32 20 Space
33 21 !
34 22
35 23 #
36 24 $
37 25 %
38 26 &
39 27 '
40 28 (
41 29 )
42 2A *
43 2B +
44 2C
45 2D 46 2E .
47 2F /
48 30 0
49 31 1
50 32 2
51 33 3
52 34 4
53 35 5
54 36 6
55 37 7
56 38 8
57 39 9
58 3A :
59 3B ;
60 3C <
61 3D =
62 3E >
63 3F ?

Dec Hex Char


64 40 @
65 41 A
66 42 B
67 43 C
68 44 D
69 45 E
70 46 F
71 47 G
72 48 H
73 49 I
74 4A J
75 4B K
76 4C L
77 4D M
78 4E N
79 4F O
80 50 P
81 51 Q
82 52 R
83 53 S
84 54 T
85 55 U
86 56 V
87 57 W
88 58 X
89 59 Y
90 5A Z
91 5B [
92 5C \
93 5D ]
94 5E ^
95 5F _

Dec Hex Char


96 60 `
97 61 a
98 62 b
99 63 c
100 64 d
101 65 e
102 66 f
103 67 g
104 68 h
105 69 i
106 6A j
107 6B k
108 6C l
109 6D m
110 6E n
111 6F o
112 70 p
113 71 q
114 72 r
115 73 s
116 74 t
117 75 u
118 76 v
119 77 w
120 78 x
121 79 y
122 7A z
123 7B {
124 7C |
125 7D }
126 7E ~
127 7F DEL

Primele 32 de caractere ASCII sunt coduri de control. Printre ele se


regsete un grup de caractere albe (whitespace). Caracterele albe dicteaz
structura fiierelor text prin mprirea acestora n linii (caracterul de linie nou newline) sau introduc spaii albe ntre cuvinte (caracterul tabulator orizontal TAB,
i caracterul de spaiu - Space). Fiierele text conin litere mici, mari, digii
numerici, semne de punctuaie. n total sunt 94 de caractere vizibile. Nu trebuie s
reinei Tabelul 1.2. Totui, este indicat s v familiarizai cu unele din

caracteristicile sale generale. n special, observai cum caracterele numerice, 0


9, apar ntr-o secven contigu de coduri, 30!" 39!" . Acelai lucru este
valabil i pentru majuscule, AZ, i litere mici, a z. De asemenea,
observai c literele mici au valoare numeric mai mare, iar diferena ntre codurile
celor dou reprezentri pentru un anumit caracter alfabetic este 20!" .
Pe lng caracterele prezente n codul ASCII trebuie s reprezentm
simboluri matematice, caractere cu indicatori de accent, litere greceti, etc.. Acest
lucru a dus la apariia unui cod nou, care n prezent se bucur de rspndire foarte
mare i este predominant n industria productoare de programe de calculator:
UTF-8, bazat pe standardul UNICODE. n segmentul dezvoltatorilor de aplicaii
web i servicii Internet a devenit practic noul standard. UTF-8 codific fiecare din
cele 1.112.069 simboluri din setul de caractere UNICODE folosind unul pn la
patru octei. Organizaia non-profit care coordoneaz dezvoltarea UNICODE i
propune s defineasc un cod standard pentru simbolurile alfanumerice utilizate n
toate rile lumii: litere, semne de punctuaie, semne monetare specifice tuturor
limbilor de pe planet. Avantajul major const n compatibilitatea cu ASCII.
Primele 128 de caractere ale setului UTF-8 sunt reprezentate pe un singur octet i
echivalente unu la unu cu cele ASCII. Astfel, orice text scris anterior cu ASCII
devine compatibil cu UTF-8.
nainte s trecei la seciunea urmtoare traducei cu ajutorul tabelului
ASCII de mai sus semnificaia irului dat la nceputul capitolului. Pentru a v uura
misiunea, putei folosi reprezentarea hexazecimal sau zecimal dedus n
paragrafele anterioare.

1.4. Exerciii
1.1. Convertii n hexazecimal urmtoarele iruri de bii:
a) 0100 0001 1110 1100
b) 1011 1111 0011 1001

c) 1010 0101 0100 0010


d) 0000 1101 0111 0110

1.2. Convertii n binar urmtoarele numere hexazecimale:


a) 78CF
b) 3A7B

c) AAAA
d) FFFF

e) 5591
f) 66DD

1.3. Convertii n zecimal urmtoarele valori hexazecimale:


a)
b)
c)
d)

A1
12
FF
80

e)
f)
g)
h)

0C
10
64
4E

i) A000
j) FFFF
k) ABCD
l) 1234

1.4. Convertii n reprezentri hexazecimale de 8 bii urmtoarele numere zecimale:


a) 100
e) 32
i) 1024
b) 128
f) 64
j) 513
c) 255
g) 16
k) 32767
d) 85
h) 8
l) 2011
1.5. Convertii n binar urmtoarele numere reale:
a)
b)
c)
d)

32.45
147.83
3.0125
255.255

e)
f)
g)
h)

15.32
7.8
63.25
18.5

1.6. Convertii n caractere ASCII urmtorul text:


Telurile sunt prea marete,
Eu sunt mic, uitat, nebagat in seama
1.7. Convertii n text urmtorul ir de bii reprezentat n hexazecimal:
45 72 61 6D 20 73 74 75 64 65 6E 74 2C 20 65 72 61 20 73 74 75 64 65 6E 74 61
20 0D 0A 65 72 61 6D 20 65 6D 69 6E 65 6E 74 2C 20 65 72 61 20 69 6D 69 6E
65 6E 74 61 20 0D 0A 73 69 20 65 72 61 75 20 73 74 65 6C 75 74 65 20 EE 6E 20
67 65 6E 65 6C 65 20 65 69 2E
Secvena 0D 0A denot trecerea la rnd nou (valabil pentru textul scris n
Windows; n Linux, rndul nou este anunat numai de 0A, caracterul standard \n).
1.8. Scriei urmtoarele numere sub form de caractere ASCII:
a) 123
b) 430
c) 15

d) 1094
e) 1452
f) 9966

1.9. Efectuai operaiile date mai jos folosind coduri ASCII hexazecimale.
Exprimai rezultatul tot ca reprezentare ASCII.
a)
b)
c)
d)

F + 20!"
F - 20!"
o - 20!"
m - 20!" 4D

e) q Q
f) g F
g) h a + 30!"
h) 4 + 6

2. ARHITECTURA CALCULATOARELOR

Capitolul de fa prezint arhitectura calculatorului, vzut ca platform de


dezvoltare a programelor. Capitolul ncepe cu descrierea structurii hardware a
sistemelor de calcul. Se pune accent pe arhitectura Intel de 32 de bii.
Funcionalitatea i organizarea intern a componentelor hardware sunt prezentate
din perspectiva procesului de dezvoltare a programelor.

2.1. Structura unui sistem de calcul


n capitolul precedent am subliniat faptul c sistemele de calcul lucreaz
numai cu iruri de cifre binare 0 sau 1 i am artat cum pot acestea reprezenta
numere sau caractere. Acest tip de informaie poart numele de date. Pe de alt
parte, sistemele de calcul nu sunt simple dispozitive de stocare a datelor. De
exemplu, pot fi comandate s execute toate operaiile realizate de noi: conversia
dintr-o baz n alta, interpretarea unui ir binar n ASCII, etc.. Fiecare operaie de
prelucrare a datelor este codificat tot sub form de ir binar. Dar pentru c nu
codific o valoare, ci o comand, aceste iruri binare se numesc instruciuni.
Instruciunile specific operaiile care trebuie efectuate de procesor. Dar atenie,
procesorul recunoate i execut numai instruciunile pentru care a fost construit.
Ansamblul instruciunilor recunoscute formeaz setul de instruciuni caracteristic
acelui tip de procesor. Un ir de instruciuni i/sau date organizate logic dup un
algoritm formeaz un program. Prin intermediul programului, utilizatorul transmite
procesorului o anumit sarcin; de exemplu, s citeasc date de la tastatur i s le
afieze la monitor.

2.1.1. Arhitectura von Neumann


La acest nivel, programul const dintr-o secven de coduri instruciune
stocate n memorie. Dac programele conin i date (majoritatea programelor
includ unele date constante), ele sunt de asemenea stocate n memorie. Cnd
sistemul de operare lanseaz n execuie un program, codurile instruciune sunt
copiate (ncrcate) de pe disc n memorie. Rularea unui program nseamn c
procesorul extrage fiecare instruciune din memorie i execut operaia codificat
de aceasta. Dac operaia implic prelucrare de date, acestea sunt extrase de
asemenea din memorie. Arhitectura unui sistem de calcul care stocheaz datele i
instruciunile mpreun ntr-o unitate de memorie separat de unitatea de procesare

se numete arhitectur von Neumann. Arhitectura poart numele celui care a


descris-o abstract ntr-un studiu din 1945, matematicianul John von Neumann (dei
conceptul a fost menionat i de ali pionieri din domeniul calculatoarelor). La
momentul respectiv, acest model de lucru n care datele de prelucrat i rezultatele
calculelor intermediare sunt stocate alturi de instruciuni ntr-o memorie comun,
era diferit de cel al calculatoarelor cu funcionalitate predeterminat, unde
programele erau stocate pe medii externe, precum cartele sau benzi perforate. n
plus, von Neumann a artat c sistemul de calcul trebuie s aib o structur fizic
fix, simpl, i s fie capabil de a executa orice fel de calcule pe baza unui control
programat, fr a fi necesar modificarea unitilor fizice. De atunci, aceste idei,
numite de obicei tehnica programelor stocate, au devenit eseniale i au fost
acceptate universal. ntr-un calculator cu arhitectur von Neumann, programul
poate fi interpretat i manipulat ca date. Acest lucru a dat natere la compilatoare i
sisteme de operare i face posibil versatilitatea calculatoarelor moderne. Tot de
aici pleac i principalul dezavantaj: un program poate fi scris astfel nct s se
poat interpreta pe sine ca date, dnd posibilitatea s se modifice pe el nsui.
Sistemele de operare moderne, precum GNU/Linux, interzic aplicaiilor s se
automodifice. Modelul von Neumann const din cinci componente majore, ilustrate
n Figura 2.1.
Unitatea de
memorie

Unitatea de
intrare

Unitatea
Aritmetic i
Logic

Unitatea de
ieire

Unitatea de
control

Figura 2.1 Arhitectura von Newmann. Sgeile ngroate reprezint ci de date. Sgeile
subiri reprezint trasee de control.

Unitatea de intrare reprezint poarta de intrare a instruciunilor i datelor n sistem.


Acestea sunt stocate n unitatea de memorie i prelucrate de unitatea aritmetic i
logic (ALU Arithmetic and Logic Unit) sub coordonarea unitii de control.
Rezultatele sunt trimise la unitatea de ieire. Structura calculatoarelor poate fi
descompus n aceste cinci uniti fundamentale chiar i n prezent.

2.1.2. Modelul bazat pe magistral


n modelul von Neumann comunicaia ntre componentele sistemului se
realizeaz prin legturi dedicate ntre perechi de componente. Aceast abordare
este relativ rigid, limitnd n mare msur scalabilitatea sistemului. Adugarea de
noi componente este dificil i implic modificarea componentelor deja existente.
De asemenea, proiectarea unei componente noi presupune cunoaterea n
amnunime a funcionrii celorlalte componente. Soluionarea acestei probleme a
venit din partea firmei Digital Equipment Corporation (DEC), care, la sfritul
anilor 60, a lansat pe pia primul calculator (PDP 11) construit n jurul unei
magistrale de sistem.

Magistrala de
sistem

CPU (ALU,
Registre i
Control)

Memorie

Intrare i
Ieire (I/O)

Magistrala de date
Magistrala de adrese
Magistrala de control

Figura 2.2 Modelul bazat pe magistral al unui calculator

Aceast rafinare a modelului von Neumann combin ALU i unitatea de


control ntr-o singur unitate funcional, numit unitate central de prelucrare
(CPU Central Processing Unit). Totodat, unitile de intrare i ieire sunt de
asemenea combinate ntr-o singur unitate de intrare i ieire. Aadar, principalele
componente hardware2 ale unui sistem de calcul sunt:
procesor sau unitate central de prelucrare;
memorie sau unitate de memorare (memoria RAM Random Access
Memory3);
unitate de intrare/ieire (unitate I/O). La aceast interfa sunt conectate
dispozitivele de intrare/ieire.
Elementul definitoriu al acestui model const n faptul c toate
componentele sistemului comunic ntre ele prin intermediul unei magistrale de
2
3

Componentele fizice ale unui sistem (structuri mecanice, cabluri, cutii, circuite, etc.).
Random se refer la faptul c timpul de acces la orice unitate de memorie este constant
i independent de locaia fizic a acesteia sau de secvenele accesurilor anterioare.

sistem. Din punct de vedere conceptual, magistrala este un mediu comun de


comunicaie ntre componentele unui sistem de calcul; fizic, este alctuit dintr-un
set de semnale care faciliteaz transferul de date i instruciuni, precum i
sincronizarea ntre componentele sistemului.

2.1.1. Magistrala de sistem


Transferul de informaii pe magistral se face pe baza unui set de reguli (un
protocol) care stabilete cine, cnd i cum se comunic pe magistral. Aadar,
magistralele sunt ansambluri de conexiuni fizice (fire) prin care circul informaie
n conformitate cu un set de reguli, avnd ca suport semnale electrice. Prin
termenul de magistral se mai nelege de regul i ansamblul de circuite
electronice care amplific puterea semnalelor electrice sau aduc nivelul acestora
(de ex., tensiunea) la valoarea standard. n funcie de informaie, semnalele
electrice sunt de trei tipuri: de date (reprezint informaia propriu zis), de adrese
(semnale care identific locaia la care este transmis informaia) i de control
(descriu aspecte legate de modul i maniera n care este transmis informaia). Nu
este obligatoriu ca o magistral s implementeze ci fizice separate pentru fiecare
din aceste trei tipuri de semnale. De exemplu, standardul PCI folosete acelai
suport fizic att pentru date ct i pentru adrese, dar la momente diferite. Tipul
informaiilor (sau direcia lor) aflate pe magistral la un moment dat este indicat
prin semnale de control. Din acest punct de vedere putem clasifica magistralele n
unidirecionale i bidirecionale. Magistralele unidirecionale pot transmite
informaiile ntr-un singur sens, cele bidirecionale n ambele sensuri. Magistrala de
adrese este unidirecional (de la procesor spre sistem) iar magistralele de date i
control sunt bidirecionale.
Un program const dintr-o secven de instruciuni stocat n memorie.
Atunci cnd procesorul este pregtit s execute urmtoarea instruciune, plaseaz
adresa locaiei de memorie care conine acea instruciune pe magistrala de adrese.
Totodat, procesorul plaseaz un semnal de citire pe magistrala de control.
Unitatea de memorie rspunde prin plasarea instruciunii pe magistrala de date, de
unde poate fi citit4 de procesor. Dac procesorul trebuie s citeasc date din
memorie, are loc aceeai secven de evenimente.
Atunci cnd procesorul trebuie s scrie5 date n memorie, acesta plaseaz
datele pe magistrala de date, adresa locaiei de memorie (la care trebuie acestea
stocate) pe magistrala de adrese, i un semnal de scriere pe magistrala de control.
Unitatea de memorie rspunde prin copierea pe magistrala de date a datelor
prezente la locaia specificat.
Dac o instruciune solicit un transfer de date din/ctre memorie,
urmtoarea instruciune nu mai poate fi citit din memorie pe aceeai magistral
4
5

Operaia de citire se traduce prin extragerea unei date stocate anterior.


Operaia de scriere se traduce prin stocarea unei date n memorie.

dect dup finalizarea acestuia. Acest conflict a dat natere la o alt arhitectur
bazat pe tehnica programelor stocate, arhitectura Harvard. n arhitectura Harvard,
programul i datele sunt stocate n memorii diferite, fiecare conectat la procesor
prin propria magistral. Acest lucru permite procesorului s acceseze simultan att
instruciunile ct i datele programului.
n calculatoarele moderne, magistrala care conecteaz procesorul de
modulele externe de memorie nu poate ine pasul cu viteza de execuie a
procesorului. ncetinirea vitezei de transfer pe magistral poart numele de
trangulare von Neumann (von Neumann bottleneck).
Interaciunea procesorului cu dispozitivele de intrare/ieire se face prin
acelai mecanism ntlnit la interaciunea procesorului cu memoria. Dac
procesorul trebuie s citeasc date de la un dispozitiv de intrare, plaseaz adresa
acestuia pe magistrala de adrese i un semnal de citire pe magistrala de control.
Dispozitivul rspunde prin ncrcarea datelor pe magistrala de date.
Cnd procesorul trebuie s trimit date la un dispozitiv de ieire, plaseaz
datele pe magistrala de date, specific adresa dispozitivului pe magistrala de adrese
i activeaz semnalului de scriere pe magistrala de control. Deoarece viteza de
rspuns a diferitelor dispozitive I/O variaz drastic fa de viteza procesorului sau
memoriei, programatorul trebuie s utilizeze tehnici speciale de programare.
O alt caracteristic de baz a magistralelor este dimensiunea acestora,
adic numrul liniilor de conectare; avem magistrale de 8 bii (8 linii de conectare),
16 bii (16 linii de conectare), etc.. Dimensiunea fiecrei magistrale este
determinat de tipul de procesor (de 8, 16, 32, 64 de bii) i determin la rndul su
numrul de locaii de memorie ce pot fi adresate (capacitatea memoriei) i structura
porturilor din dispozitivele de intrare/ieire. De exemplu, o magistral de 32 de bii
poate adresa o memorie RAM de 2!" = 2!" 2! = 4!GB.

2.1.2. Unitatea de intrare/ieire


Majoritatea programelor trebuie s comunice cu dispozitive I/O. Tipul
dispozitivelor I/O variaz foarte mult. Unele sunt menite s interacioneze cu
utilizatorul (de exemplu, tastatura, monitorul, mouse-ul), altele s permit
programelor interaciunea cu uniti hardware (de exemplu, stocarea un fiier pe
disc sau citirea unui fiier din reea). Toate aceste dispozitive se comport diferit i
cerinele lor de sincronizare difer drastic de la unul la altul. Din moment ce
programarea dispozitivelor I/O este dificil, dar aproape toate programele au
nevoie de acestea, software-ul care gestioneaz dispozitivele I/O este inclus n
sistemul de operare. Sistemele Linux includ un set bogat de funcii care permit
programatorilor de aplicaii s efectueze operaii de intrare/ieire. ntr-un capitol
viitor vom apela i noi la serviciile puse la dispoziie de sistemul de operare pentru
ndeplinirea unor operaii de intrare/ieire.
Aadar, pe lng componentele hardware, un sistem de calcul dispune i de

o component software6 fundamental: sistemul de operare. De fapt, termenul


arhitectura calculatoarelor definete grania dintre hardware i software, i putem
afirma, fr s fim acuzai de partizanat, c arhitectura calculatorului reprezint
sistemul de calcul vzut de un programator n limbaj de asamblare.

2.1.3. Memoria
Memoria sistemului, numit i memorie principal, este folosit la stocarea
informaiilor (instruciuni i date) n format binar i reprezint sursa sau destinaia
tuturor informaiilor. Toate informaiile iniiale i rezultatele prelucrrilor sunt
ncrcate, generate sau stocate temporar n memorie.
Memoria este organizat ca o colecie de locaii de memorare, numerotate
consecutiv, ncepnd cu zero. Unitatea de baz a memoriei este octetul. Ne putem
imagina memoria ca ir liniar de octei suprapui, asemenea unui dulap cu sertare
numerotate, fiecare sertar avnd capacitatea de 8 bii (Figura 2.3). Numrul
fiecrui sertar este ceea ce numim n general adres fizic, iar sertarul, n
programare, se numete locaie de memorie. Adresele permit identificarea fiecrui
octet din memorie. Mulimea total a adreselor fizice constituie spaiul adreselor
fizice, iar numrul de bii dintr-o locaie de memorie reprezint dimensiunea
locaiei sau formatul memoriei. n cazul nostru, considerm dimensiunea locaiei
ca fiind de 8 bii.
Figura 2.3 prezint o memorie adresat printr-o magistral de 20 de bii.
Dimensiunea magistralei, adic numrul liniilor de conectare, determin numrul
de locaii de memorie ce pot fi adresate. n acest caz, adresa fizic a primului octet,
de jos n sus, este reprezentat prin 20 de bii de 0.
FFFFFH
.
.
.
00003H
00002H
1 0 0 0 1 0 0 1 00001H
00000H
0
7

Figura 2.3 Reprezentarea memoriei principale

n programare ntotdeauna numrm ncepnd cu zero. Urmtorul octet are adresa


1, .a.m.d, pn la adresa ultimului octet ce poate fi adresat cu 20 de bii, adresa cu
toi biii de 1. n Figura 2.3 adresele sunt reprezentate n hexazecimal. Numrul
total de octei de memorie este 2!" = 1048576. n binar, prefixul kilo este asociat
6

Componentele imateriale (programe de sistem i aplicaii).

cu 1024 = 2!" , prefixul Mega i Giga cu 1048576 = 2!" , respectiv


1073741824 = 2!" . n concluzie, Figura 2.3 ilustratreaz o memorie RAM cu
capacitatea de 1 MB.
Pe viitor va fi foarte important s stpnii la perfecie puterile lui 2
prezentate n Tabelul 2.1. V vor ajuta s evaluai rapid capacitatea de memorie
adresabil cu o magistral de 16 sau 32 de bii.
Memoria se msoar n uniti de ordinul kilobytes (kB), megabytes (MB),
gigabytes (GB).
Tabelul 2.1 Puteri ale lui 2 n binar i zecimal

Binar
1
10
100
1000
10000
100000
1000000
10000000
100000000
1000000000
10000000000
100000000000
1000000000000
10000000000000
100000000000000
1000000000000000
10000000000000000
100000000000000000
1000000000000000000
10000000000000000000
100000000000000000000

Puteri ale lui 2


2!
2!
2!
2!
2!
2!
2!
2!
2!
2!
2!"
2!!
2!"
2!"
2!"
2!"
2!"
2!"
2!"
2!"
2!"

Zecimal
1
2
4
8
16
32
64
128
256
512
1024
2048
4096
8192
16384
32768
65536
131072
262144
524288
1048576

2.2. Arhitectura IA-32


n aceast carte studiem arhitectura procesoarele Intel de 32 de bii sau
compatibile. Numrul acestora este impresionant. Intel a produs microprocesoare
nc din 1969. Primul lor microprocesor a fost 4004. Era un procesor de 4 bii
(capacitatea de memorare a registrelor specific tipul de procesor). A fost urmat
de 8080 i 8085. Dezvoltarea acestor microprocesoare a dus la apariia arhitecturii

Intel (IA Intel Architecture).

2.2.1. Arhitectur i microarhitectur


Primul procesor al arhitecturii Intel este 8086, aprut n 1978. Avea o
magistral de adrese de 20 de bii i o magistral de date de 16 bii. Deoarece
registrele interne i magistrala de date erau de 16 bii, 8086 a fost considerat primul
procesor de 16 bii. Magistrala de adrese de 20 de bii i permitea s adreseze 1 MB
de memorie.
8088 a fost versiunea mai ieftin a lui 8086. Reducerea preului s-a obinut
prin folosirea unei magistrale de date de 8 bii. Cu excepia acestei deosebiri, 8088
este identic cu 8086.
80186 a fost varianta mai rapid a lui 8086. Dispunea de aceleai
magistrale, dar un set mbuntit de instruciuni. ns 80186 nu a fost niciodat
foarte ndrgit de productorii de calculatoare.
Adevratul succesor al lui 8086 a fost 80286, introdus n 1982. Magistrala
de adrese de 24 de bii i permitea s adreseze o memorie de 16 MB i includea
cteva posibiliti de protecie a memoriei.
Primul procesor de 32 de bii, 80386, apare n 1986. Are o magistral de
adrese de 32 de bii i o magistral de date tot de 32 de bii. Spaiul de memorie
adresabil a crescut la 4 GB i a fost introdus mecanismul paginrii (vom prezenta
detalii cu privire la acest aspect ceva mai trziu). Paginarea a permis un model
liniar de memorie - spaiu liniar, continuu de adrese7 (modelul de memorie
prezentat de noi n seciunea precedent) i prin el, posibilitatea portrii
sistemelor de operare din familia Unix pe arhitecturi Intel.
Intel 80486 apare n 1989 ca variant mbuntit a lui 80386. Pentru
prima dat un procesor Intel ncorpora unitatea de calcul aritmetic n virgul
mobil (d posibilitatea efecturii operaiilor cu numere reale). Pn atunci,
unitatea de calcul n virgul mobil era prezent sub forma unui coprocesor extern.
80486 dispune i de alte mbuntiri tehnologice: unitile de decodificare i
execuie a instruciunilor cu capacitate de lucru n paralel, 8 kB L1 cache, prezena
unui cache L2. Versiunile ulterioare includ tehnologii de economisire a consumului
de energie (power saving), utile n special laptop-urilor.
Pentium/Pentium Pro aduc foarte puine elemente noi. n principal mresc
viteza de execuie.
Pentium MMX aduce suport MMX (MultiMedia eXtensions) - instruciuni
ce mresc viteza operaiilor grafice.
Pentium II este un Pentium Pro cu suport MMX. Pentium III, n esen un
Pentium II mai rapid, introduce arhitectura SIMD (Single Instruction Multiple
Data) pentru execuia concurent a mai multor operaii n virgul mobil.
7

Adresele pornesc de la zero i se succed, ntr-un mod liniar, fr goluri sau ntreruperi,
pn la limita superioar impus de numrul total de bii dintr-o adres logic.

Pentium 4 mbuntete aceste caracteristici. Tot de la Pentium 4 apar


primele modele de 64 de bii (arhitectur botezat iniial EMT64T). Pentium 4 a
fost urmat de Pentium Xeon, Pentium II Xeon, Pentium Core, Celeron, Core Duo,
Core 2 Duo, Core i3, Core i5, Core i7 i multe altele. Toate acestea au fost
fabricate numai de Intel. Alte companii, n special AMD, au fabricat propriile
procesoare compatibile cu cele produse de Intel.
Arhitectura
n capitolele precedente am afirmat c programarea n asamblare necesit
cunotine legate de procesor. Legate de se refer la ce face un procesor, spre
deosebire de cum face un procesor. Perspectiva programatorului include, printre
altele, numrul i tipul de registre, setul de coduri instruciune recunoscut, modul
de adresare a memoriei, prezena sau lipsa unor uniti de uz special precum
coprocesorul matematic (cu propriile instruciuni i registre). Toate aceste lucruri
sunt definite de productor, iar definiiile, luate mpreun, formeaz arhitectura
procesorului.
The architecture, as I see it, is the high-level specs of the processor. This consists
of the instructions set, memory structure, memory segmentation, register structure,
I/O space, interrupt mechanism, instruction operands, memory addressing modes,
and so on.
Stephen Morse8
Cu timpul, arhitectura este mbogit cu noi instruciuni, registre sau alte
caracteristici. De obicei, noile caracteristici nu modific modul de lucru anterior, ci
adaug noi opiuni. Acest lucru asigur compatibilitatea noilor platforme cu
programele scrise anterior (backward compatibility). Anterior nseamn c
programe scrise pentru 8086 pot rula i astzi pe un Core i7.
Cteodat, pe lng adugarea periodic de noi instruciuni, arhitecturile
sufer schimbri mai importante. Am vzut c, n 1986, o dat cu apariia
procesorului 80386, pe lng introducerea unor noi instruciuni i moduri de
operare, Intel dubleaz capacitatea registrelor. Se fcea astfel trecerea la arhitectura
de 32 de bii. Aceast arhitectur poart numele de IA-32 (i386, x86-32). Este
arhitectura studiat de noi n aceast carte. n 2003, arhitectura x86 se extinde
iari, de data aceasta la 64 de bii; noi instruciuni, noi moduri de operare, registre
de capacitate dubl. Printele acesteia este principalul competitor Intel, AMD, care
o numete AMD64. La acea dat, Intel lansase deja propria arhitectur de 64 de
bii, numit IA-64 (Itanium), dar incompatibil cu precedentele procesoare x86 (set
de instruciuni diferit de x86). Pentru c nu se bucur de foarte mult succes,
compania Intel este nevoit s produc o familie de procesoare compatibil cu
arhitectura i specificaiile AMD. Termenul generic pentru noua arhitectur de 64
de bii, compatibil cu x86, este x86-64.
8

Printele arhitecturii microprocesorului Intel 8086.

Arhitectura x86-64 include arhitectura IA-32, care la rndul su include


vechea arhitectur x86 de 16 bii. Totui, la proiectarea unei aplicaii,
programatorul trebuie s tie care sunt procesoare compatibile cu instruciunile
folosite. Aplicaia nu va rula pe procesoarele dinaintea apariiei instruciunilor
respective.
Microarhitectura
Problemele ridicate de pstrarea compatibilitii cu programele dezvoltate
anterior limiteaz creterea performanelor prin mijloacele arhitecturii. Dou din
cele mai importante criterii de performan constau n numrul total de instruciuni
pe care un procesor este capabil s l execute ntr-un anumit interval de timp
(throughput) i consumul de energie. Numrul de instrucini pe unitate de timp
trebuie s fie ct mai mare (vitez de procesare mai mare), iar consumul de energie
ct mai mic. n cazul celui din urm, motivul este mai subtil. O bun parte din
energia utilizat de procesor se pierde sub form de cldur; aceste pierderi, dac
nu sunt minimizate, pot leza procesorul i componentele din vecintate. Proiectanii
caut ntotdeauna soluii care s permit efectuarea acelorai sarcini cu consum mai
mic de energie. Unele din acestea permit procesorului s intre n starea de repaus
(stand-by) n momentele n care nu este folosit.
Puterea de procesare a crescut prin dezvoltarea unor mecanisme precum
citirea anticipat a instruciunilor (prefetching), execuie n benzi de prelucrare
(pipelining), execuia paralel (hyper threading), predicia salturilor (branch
prediction), execuia speculativ, memorii intermediare L1 i L2 (cache), i multe
altele. Unele tehnici reduc sau elimin blocajele din interiorul procesorului, astfel
nct acesta s lucreze permanent (aici, un rol important l are mecanismul de lucru
cu memoria), altele permit procesorului s execute mai multe instruciuni n acelai
timp. mpreun, aceste mecanisme fizice prin care procesorul ndeplinete
operaiile codificate n instruciuni, formeaz microarhitectura procesorului. n
vederea creterii performanelor, Intel i AMD reproiecteaz constant
microarhitecturile procesoarelor. n acest context se nscriu i eforturile de
mbuntire a tehnicilor de fabricaie a pastilelor de siliciu, tehnici ce permit
creterea numrului de tranzistoare plasate pe un singur cip. Toate numele exotice,
NetBurst, Core, Nehalem, Sandy Bridge, Ivy Bridge, indic modificri aprute n
microarhitectur.

2.2.2. Structura de principiu a procesorului


n aceast seciune detaliem modul n care programatorul vede structura
procesorului i modul n care procesorul interacioneaz cu memoria principal.
Figura 2.4 prezint schema bloc general a unui procesor. Unitile sunt conectate
prin magistrale interne. Reinei c aceasta este o diagram extrem de simplificat.
Procesoarele reale sunt mult mai complexe, dar conceptele generale discutate n
acest capitol se aplic tuturor.

Indicatorul de instruciune
Memorie L1 Cache
Registrul de instruciune

Unitatea de control
Registre
Unitatea
Aritmetic i
Logic
Interfaa cu
magistralele

Registrul indicatorilor de
stare

ctre magistralele de adrese, date i control


Figura 2.4 Structura general a unui procesor

n continuare descriem pe scurt fiecare unitate n parte. Componentele de


interes special pentru programator sunt prezentate pe larg puin mai trziu.
Unitatea de interfa cu magistrala
Unitatea de interfa cu magistrala de sistem este mijlocul prin care
procesorul comunic cu restul componentelor din sistem memorie i dispozitive
de intrare/ieire. Unitatea conine circuite ce permit plasarea adreselor pe
magistrala de adrese, plasarea semnalelor de citire i scriere pe magistrala de
control, citirea i scrierea datelor pe magistrala de date. De obicei, unitatea de
interfa cu magistrala este conectat la uniti externe de control al magistralelor,
care, la rndul lor, sunt conectate la memorie sau la diferite dispozitive I/O (de ex.,
SATA, PCI-E, etc.). Pentru programator, aceste unitile externe de control sunt
invizibile.
Unitatea de management a memoriei
ncepnd cu procesoarele de 16 bii apare noiunea de adres logic.
Adresele generate de sistemul de operare sunt considerate adrese logice i
totalitatea acestora formeaz spaiul de adrese logice. Aceste noiuni constrasteaz
cu cele de adres fizic (aflat pe magistrala de adrese) i spaiu de adrese fizice
discutate n seciunea dedicat memoriei principale. Din punct de vedere al

numrului de adrese, cele dou spaii, al adreselor fizice i logice, pot fi egale sau
inegale. Ca urmare, procesorul trebuie s dispun de un mecanism de conversie a
adreselor (un mecanism de translatare a adreselor logice n adrese fizice). La
procesoarele Intel x86 mecanismul de translatare este inclus pe acelai substrat de
siliciu cu microprocesorul.
Datorit mecanismului de translatare a adreselor, modul de organizare al
memoriei poate fi definit prin software, de ctre sistemul de operare. Dac
procesorul permite adresarea liniar a ntregului spaiu de adrese fizice (adresele
fizice ncep de la 0 i avanseaz liniar), atunci i sistemul de operare poate organiza
memoria ca structur liniar (adresele logice ncep de la 0 i avanseaz liniar). Pe
de alt parte, atunci cnd procesorul poate adresa liniar numai anumite segmente de
memorie, segmente cu lungimi mai mici dect capacitatea total a memoriei,
vorbim de organizare segmentat. n acest caz, spaiul de adrese logice este
mprit n mai multe spaii cu adresare liniar, fiecare de lungime diferit, i o
adres logic este calculat ca sum ntre adresa de nceput a unui segment (adresa
de nceput a unui bloc de memorie) i un deplasament n cadrul acestuia.
Memorii intermediare
Cnd procesorul are nevoie de informaii aflate n memoria principal
trimite ctre unitatea de management a memoriei o cerere de citire memorie.
Unitatea de management a memoriei trimite cererea respectiv la memorie i,
atunci cnd informaia se afl pe magistrala de date, anun procesorul. Lungimea
ntregului ciclu procesor, controler de memorie, memorie, (napoi la) procesor -,
variaz n funcie de viteza memoriei i a magistralei de date. Aadar, o memorie
cu timp de acces mai mic contribuie semnificativ la performana sistemului.
Performana memoriei este cuantificat prin intermediul a doi parametrii:
timpul de acces - perioada de timp necesar memoriei s extrag datele din locaia
adresat, i timpul unui ciclu la memorie - timpul minim ntre dou operaii
succesive de acces la memorie.
Totui, viteza de lucru a memoriei i eforturile de cretere a acesteia reprezint
numai o faet a problemei. Timpul necesar datelor i intruciunilor s parcurg
traseul dintre memorie i procesor este mult mai mare dect timpul consumat de
procesor pentru prelucrarea acestora (acest fenomen se numete trangulare von
Neumann). De aceea, ntre procesor i memoria principal a fost introdus o
memorie intermediar, numit cache. Memoria cache este o memorie foarte rapid,
de mici dimensiuni (tipic, mai puin de 1MB), plasat foarte aproape de procesor.
Este proiectat s pstreze datele i instruciunile solicitate frecvent de procesor.
Pentru c preluarea datelor din cache are loc numai ntr-o fraciune din timpul
necesar accesrii memoriei principale, prezena memoriei cache salveaz foarte
mult timp. Principiul pe care se bazeaz mecanismul cache este regula 80/20, care
spune c, din toate programele, informaiile i datele din calculator, aproximativ
20% sunt utilizate 80% din timp. n consecin, este foarte probabil ca datele i

instruciunile folosite de procesor la un moment dat s fie necesare din nou, la


puin timp dup.
Memoria cache este ca o list fierbinte (hot list) de instruciuni necesare
procesorului. Unitatea de management a memoriei salveaz n cache fiecare
instruciune solicitat de procesor; la o nou cerere similar, procesorul primete
instruciunea din cache (cache hit). Totodat, acea instruciune avanseaz ctre
partea superioar a listei. Atunci cnd memoria cache este plin i procesorul
solicit o nou instruciune, sistemul suprascrie instruciunile care nu au fost
utilizate de cea mai lung perioad de timp. Acest mecanism este valabil i pentru
date. n acest fel, informaiile cu prioritate ridicat, folosite des, rmn n cache, n
timp ce informaiile mai puin utilizate sunt eliminate.
n prezent, memoriile cache sunt ncorporate chiar n cipul procesorului.
Cum majoritatea procesoarelor conin mai multe nuclee de procesare, numite coreuri, n funcie de proximitatea sa fa de acestea, memoria cache are atribuit un
nivel. Memoria cache cea mai aproape de un nucleu se numete cache de nivel 1
(L1), urmtorul nivel de cache este notat L2, apoi L3, .a.m.d.. De exemplu, un
procesor Core i7 Quad dispune de 64 kB cache L1 (32 kB pentru instruciuni, 32
kB pentru date), 1 MB cache L2, ambele per nucleu, i de 8 MB cache L3, partajat
ntre nuclee. Toate pe pastila procesorului. Totodat, majoritatea procesoarelor
moderne folosesc dou memorii cache L1, una pentru date i una pentru
instruciuni, n configuraie arhitectural tip Harvard.
Conceptul de cache se aplic nu numai memoriei principale, ci i mediului
de stocare. De exemplu, sistemul de operare poate folosi memoria principal ca
memorie intermediar pentru disc. n acest caz, memoria principal nregistreaz
cele mai recente date citite de procesor de pe disc sau stocheaz date ce vor fi
scrise de procesor pe disc la un moment viitor. Reinem aadar c termenul cache
se poate referi att la memorie ct i la alte tehnologii de stocare.
Registre
Unele instruciuni au nevoie ca datele prelucrate de acestea s fie
memorate chiar n interiorul procesorului. Acest lucru se realizeaz prin
intermediul unor locaii de memorie numite registre. Registrele sunt locaii de
memorie volatil9 aflate n interiorul procesorului. Procesorul acceseaz mult mai
rapid datele stocate n registre dect pe cele aflate n memoria principal sau cache.
Pe de alt parte, numrul de registre este limitat. Dimensiunea registrelor, n bii,
determin i arhitectura procesoarelor - procesoare de 16, 32 sau 64 de bii. n
aceast carte ne referim la arhitectura procesoarelor Intel de 32 de bii - registrele
interne au capacitatea de 4 octei.
Indicatorul de instruciune
Indicatorul de instruciune este registrul care memoreaz adresa urmtoarei
9

n lipsa unei surse de energie, informaiile sunt pierdute. Memoria RAM este tot o
memorie volatil.

instruciuni din secvena de cod executat.


Registrul de instruciune
Instruciunile sunt simple abloane (pattern) de bii. Acest registru conine
instruciunea executat n acel moment. Modelul su de bii determin unitatea de
execuie s comande celorlalte uniti din procesor o anumit operaie. Odat ce
aciunea a fost finalizat, biii instruciunii (biii prin care aceasta a fost codificat)
din registrul de instruciune pot fi nlocuii i procesorul va efectua operaia
specificat de biii noii instruciuni.
Unitatea de execuie
Biii din registrul de instruciune sunt decodificai de unitatea de execuie.
Aceasta genereaz semnalele care comand celorlalte uniti din procesor
efectuarea aciunilor specificate de instruciune. De obicei este implementat sub
forma unei maini cu stri finite care conine decodoare, multiplexoare i alte
circuite logice.
Unitatea aritmetic i logic (ALU)
ALU denot circuitele electronice care efectueaz operaii aritmetice i
logice pe grupuri de bii de date.
Registrul indicatorilor de stare
Registrul indicatorilor de stare semnaleaz anumite evenimente aprute n
cadrul operaiilor aritmetice i logice. De exemplu, operaia de adunare poate
produce un transport. Un anumit bit din registrul indicatorilor de stare va fi setat n
zero (nu exist transport) sau unu (exist transport) de fiecare dat cnd ALU a
finalizat o astfel de operaie.

2.2.3. Funcionarea procesorului


Setul de instruciuni n idiom binar care poate fi executat de un procesor
formeaz limbajul main al acelui tip de procesor. Instruciunile main sunt
iruri de numere binare pe care procesorul le decodific i execut foarte rapid.
Fiecare tip de procesor are propriul limbaj main. Din aceast cauz, programele
scrise pentru procesoarele Intel x86 nu pot rula pe alte tipuri de procesoare
(POWER, SPARC, Intel Itanium, ARM).
Procesorul poate fi privit ca dispozitiv ce execut la nesfrit urmtorul
proces:
Extragere instruciune din memorie (fetch);
Decodificare instruciune (decode);
Execuie instruciune.

Acest proces este numit ciclu de extragere-decodificare-execuie sau,


simplu, ciclu de execuie.
Ciclu de execuie
Extrage

Decodific

Execut

Extrage

Decodific

Execut

timp

La primele procesoare, de exemplu 8080, etapele ciclului de execuie se


realizau secvenial. ncepnd cu 80286, acestea sunt efectuate n paralel. n plus,
fiecare microarhitectur nou adug diferite tehnici de eficientizare. Totui, din
perspectiva programatorului, mecanismul prezentat aici este tot ce intereseaz.
Extragerea unei instruciuni din memorie presupune plasarea adresei sale
pe magistrala de adrese i activarea semnalului de citire pe magistrala de control
(lucru care indic intenia de citire a locaiei respective). Dup iniierea cererii de
citire procesorul ateapt apariia instruciunii pe magistrala de date.
Decodificarea presupune identificarea instruciunii extrase. Pentru
facilitarea acestui proces, instruciunile main folosesc o schem particular de
codificare (un cod de operaie).
Execuia instruciunii nseamn prelucrarea operaiei aritmetice sau logice.
Execuia instruciunilor este sincronizat cu ajutorul unui generator de tact, numit
ceas10. Generatorul de tact pulseaz la o frecven fix cunoscut sub numele de
frecvena ceasului (dei se utilizeaz uzual i termenul de frecvena procesorului).
Ceasul nu contorizeaz minute sau secunde, doar cicleaz la o frecven
constant. Circuitele electronice din procesor folosesc aceast frecven pentru a
executa operaiile corect, aa cum un dirijor dicteaz ritmul unei piese. Frecvena
impulsurilor de tact determin viteza de execuie a instruciunilor. Fiecare ciclu de
execuie a unei instruciuni poate fi descompus ntr-o serie de operaii elementare
efectuate n paralel, pe durata unui singur impuls de tact (ciclu de tact). O
asemenea operaie elementar (o singur prelucrare numeric a informaiei sau un
transfer de date ntre procesor i memorie) se numete micro-operaie. Numrul de
cicli de tact necesari execuiei unei instruciuni depinde att de instruciune ct i
de modelul i generaia procesorului. O frecven mai mare nu nseamn neaprat
i numr de operaii pe secund mai mare, depinde de arhitectura procesorului.

2.2.4. Registrele procesorului


Arhitectura IA-32 pune la dispoziia programatorului zece registre de 32 de
bii i ase registre de 16 bii. Aceste registre sunt grupate n urmtoarele categorii:
registre de uz general, registre de control i registre de segment. Registrele de uz
general sunt la rndul lor mprite n registre de date, registre indicator i registre
10

Generator electronic pilotat de un cristal de cuar, care asigur stabilitatea frecvenei la


variaia tensiunii de alimentare i a temperaturii.

index.
Registre de date
32 de bii

EAX
EBX
ECX
EDX

31

16

15

8 7
AH
BH
CH
DH

0
AL
BL
CL
DL

16 bii

AX
BX
CX
DX

Figura 2.5 Registre de date

Arhitectura IA-32 pune la dispoziie patru registre de date de 32 de bii


fiecare destinate operaiilor aritmetice i logice, dar nu numai. Aceste patru registre
pot fi folosite dup cum urmeaz:
4 registre de 32 de bii: EAX, EBX, ECX, EDX; sau
4 registre de 16 bii: AX, BX, CX, DX; sau
8 registre de 8 bii: AH, AL, BH, BL, CH, CL, DH, DL (H = High, L =
Low).
Registrele de date pot fi folosite fr constrngere n majoritatea
instruciunilor aritmetice i logice. Totui, cnd sunt executate anumite instruciuni,
unele registre din acest grup au funcii speciale. De exemplu, registrele EAX (AX),
denumit acumulator, i EDX (DX), au rol prestabilit n instruciunile de nmulire,
mprire i de transfer I/O. ECX (CX) este utilizat drept numrtor pentru
operaiile de deplasare, rotaie, bucle, repetiii de instruciuni.
Registre index i indicator
31

16

15

ESI
EDI

0
SI
DI

Source Index
Destination Index

Figura 2.6 Registre index


31
ESP
EBP

16

15

0
SP
BP

Stack Pointer
Base Pointer

Figura 2.7 Registre indicator

Arhitectura IA-32 nglobeaz dou registre index i dou registre indicator.


Acestea pot fi folosite ca registre de 16 sau 32 de bii. Registrele index au rol
principal n prelucrarea instruciunilor, dar pot fi folosite i ca registre de date.
Registrele indicator sunt folosite n special pentru lucrul cu stiva.

Stiva este o zon din memoria principal organizat dup principiul LIFO
(Last In First Out), folosit ca mijloc de depozitare a datelor temporare. Stivele
sunt strict necesare n lucrul cu subprograme (proceduri i funcii), cnd registrele
interne trebuie eliberate n vederea execuiei unei funcii care va suprascrie
registrele cu propriile sale date. Eliberarea registrelor se face prin salvarea lor n
stiv, ntr-o anumit ordine, i refacerea lor din stiv la revenirea n programul
apelant.
Registre de control
31
EIP

16

15

0
IP

Instruction Pointer

Figura 2.8 Indicatorul de instruciune

Acest grup de registre const din dou registre de 32 de bii: registrul


indicator de instruciune i registrul indicatorilor de stare.
Procesorul folosete registrul indicator de instruciune ca s memoreze
adresa urmtoarei instruciuni ce va intra n ciclul de execuie. Cteodat, acest
registru este denumit registru contor de program. Registrul indicator de instruciune
poate fi folosit ca registru de 16 (IP) sau 32 de bii (EIP), n funcie de mrimea
adreselor. Cnd o instruciune este extras din memorie, registrul indicator de
instruciune este reiniializat automat cu adresa urmtoarei instruciuni. Registrul
poate fi modificat i de o instruciune care transfer controlul execuiei la alt
locaie n program.

31

16 15

0 0 0 0 0 0 0 0 0 0 ID VIP VIF AC VM RF 0 NT IOPL OF DF IF TF SF ZF 0 AF 0 PF 1 CF

Valorile de de 0 i 1 n gri sunte rezervate Intel.

Indicatori de stare
CF = Carry Flag
PF = Parity Flag
AF = Auxiliary Carry Flag
ZF = Zero Flag
SF = Sign Flag
OF = Overflow Flag

Indicatori de control
DF = Direction Flag

Indicatori de sistem
TF = Trap Flag
IF = Interrupt Flag
IOPL = I/O Privilege Level
NT = Nested Task
RF = Resume Flag
VM = Virtual 8086 Mode
AC = Alignment Check
VIF = Virtual Interrupt Flag
VIP = Virtual Interrrupt Pending
ID = ID flag

Registrul indicatorilor de stare poate fi considerat ca fiind de 16 (FLAGS)


sau 32 de bii (EFLAGS). Registrul FLAGS este folosit atunci cnd se execut cod
compatibil 8086.
Registrul EFLAGS conine 6 indicatori de stare, 1 indicator de control i
10 indicatori de sistem. Biii acestui registru pot fi setai (1 logic) sau nu (0 logic).
Setul de instruciuni IA-32 conine instruciuni capabile s seteze sau s tearg
valoarea unora dintre indicatori. De exemplu, instruciunea clc terge valoarea
indicatorului de transport (Carry Flag), iar instruciunea stc o seteaz.
Cei 6 indicatori de stare semnalizeaz un eveniment specific aprut n urma
execuiei ultimei instruciuni aritmetice sau logice. De exemplu, dac o instruciune
de scdere produce rezultat zero, procesorul seteaz automat indicatorul de zero,
ZF (Zero Flag ia valoarea 1). Vom discuta n detaliu indicatorii de stare atunci cnd
vom prezenta instruciunile aritmetice i logice. Aici mai amintim faptul c
indicatorul de direcie (DF) se difereniaz de ceilali indicatori prezeni n registrul
EFLAGS. Prin intermediul lui, programatorul poate specifica procesorului cum
trebuie s judece anumite instruciuni; aadar, programatorul semnalizeaz ceva
procesorului i nu invers. Rolul acestui indicator va fi discutat n detaliu n
seciunea dedicat instruciunilor de operare pe iruri.
Cei zece indicatori de sistem controleaz modul n care opereaz
procesorul. De exemplu, setarea indicatorului de mod virtual, VM, foreaz
procesorul s emuleze un 8086, iar posibilitatea setrii i tergerii indicatorului ID
indic faptul c procesorul poate furniza programelor informaie cu privire la
productorul procesorului, familia din care face parte, modelul, etc., prin
intermediul instruciunii CPUID.

2.2.5. ntreruperile
Dispozitivele de intrare/ieire pun la dispoziia sistemelor de calcul
mijloacele prin care acestea pot interaciona cu exteriorul. Un dispozitiv poate fi un
dispozitiv de intrare (de ex., tastatura, mouse-ul), un dispozitv de ieire (de ex.,
imprimanta, monitorul), sau un dispozitiv de intrare i ieire (de ex., discul).
Calculatoarele folosesc dispozitive de intrare/ieire, numite i periferice, din dou
motive principale: s comunice cu exteriorul i s stocheze date. Dispozitive ca
imprimanta, tastatura, modemurile, plcile de reea sunt folosite n comunicaia cu
exteriorul, iar discul la stocarea informaiei. Dei scopurile sunt diferite, sistemul
comunic cu aceste dispozitive prin intermediul aceleiai magistrale de sistem.
Dispozitivele de intrare/ieire nu sunt conectate direct la magistrala de sistem;
comunicaia dintre sistem i periferic este gestionat de un controler de
intrare/ieire.
Procesoarele acceseaz registrele interne ale controlerului de intrare/ieire
prin porturile de intrare/ieire. Un port I/O reprezint adresa unui registru din

controlerul I/O. Procesoarele pot mapa porturile I/O la adrese de memorie,


mecanism numit memory-mapped I/O, sau pot folosi un spaiu de adrese I/O
separat de spaiul adreselor de memorie, tehnic numit isolated I/O.n primul caz,
scrierea la un port I/O este similar cu scrierea ntr-o locaie de memorie. n al
doilea caz, spaiul de adrese I/O este accesat cu ajutorul unor instruciuni speciale,
numite instruciuni de intrare/ieire. Setul de instruciuni IA-32 furnizeaz dou
astfel de instruciuni: IN i OUT. Instruciunea IN este folosit pentru citirea
portului I/O, iar instruciunea OUT pentru scrierea portului I/O.
Procesoarele pot folosi simultan ambele strategii. De exemplu, imprimanta
i tastatura sunt mapate n spaiul de adrese I/O folosind strategia isolated I/O, iar
monitorul este mapat la un set de adrese de memorie folosind strategia memorymapped I/O.
Arhitectura IA-32 pune la dispoziie un spaiu de adrese I/O de 64 kB.
Acest spaiu de adrese poate fi folosit pentru porturi I/O de 8, 16 i 32 de bii.
Totui, combinaia nu poate depi 64 kB. Sistemele de operare pun la dispoziia
aplicaiilor i utilizatorilor rutine (secvene de instruciuni) de acces la dispozitivele
I/O. Sistemele Linux i Windows implementeaz un set astfel de rutine. Pe lng
acestea, se pot folosi i rutine prezente n BIOS. BIOS-ul este un soft rezident ntro memorie ROM aflat pe placa de baz i const dintr-o colecie de rutine care
controleaz dispozitivele I/O.

2.3. Exerciii
2.1. Care este diferena ntre microarhitectura i arhitectura procesorului?
2.2. Descriei ciclul de execuie.
2.3. Ce rol are memoria principal n structura de ansamblu a unui calculator?
2.4. Ce nseamn arhitectur von Neumann? Care este problema principal a
acesteia?
2.5. Care processor este ultimul reprezentant al arhitecturii IA-32?
2.6. Dac un procesor poate folosi o magistral de adrese de 64 de linii, care este
mrimea spaiului de memorie adresabil? Exprimai adresa ultimei locaii de
memorie n hexazecimal.
2.7. Care sunt registrele de uz general la procesoarele Intel de 32 de bii?

3. LIMBAJUL DE ASAMBLARE

Acest capitol prezint nivelurile la care utilizatorul poate interaciona cu


sistemul de calcul i definete limbajul de asamblare n contextul acestei
interaciuni. Totodat se explic pe ce nivel se regsete limbajul de asamblare n
ierarhia limbajelor de programare. Deoarece reiese c limbajul de asamblare
reprezint o etap parcurs de orice compilator n procesul de generare a codului
obiect, prezentm procesul de compilare a unui program scris n C. Capitolul se
ncheie cu o descriere succint a avantajelor i dezavantajelor asociate cu
programarea n diferite limbaje de programare. Aceast seciune are rolul s ofere
motivaia necesar studierii limbajului de asamblare i s demonstreze avantajele
acestuia.
Nivel 5

Aplicaie
Crete nivelul
de
abstractizare

Nivel 4

Limbaj de nivel nalt


(C, C++, Java)

Indepentent de
sistem

Nivel 3

Limbaj de asamblare
Dependent de
sistem

Nivel 2

Limbaj main

Nivel 1

Apeluri de sistem
Nivel 0

Hardware

Figura 3.1 Nivelurile de interaciune cu sistemul de calcul

Figura 3.1 prezint nivelurile la care utilizatorul poate interaciona cu un


sistem de calcul.
La nivelul programelor de aplicaie utilizatorul interacioneaz cu sistemul
de calcul prin interfaa pus la dispoziie de o anumit aplicaie, fie aceasta un
editor de text, un joc, etc.. Utilizatorul nu trebuie s cunoasc n detaliu cum
funcioneaz sistemul, problema se reduce numai la tiina de a folosi aplicaia.
Presupunnd c este vorba de un editor de text, tot ce trebuie s stpneasc
utilizatorul, din punct de vedere tehnic, sunt combinaiile de taste care introduc,
terg, copiaz text, etc..
Nivelul urmtor presupune cunoaterea unui limbaj de programare de nivel
nalt, precum C sau Java. Un utilizator care interacioneaz cu sistemul de calcul la
acest nivel trebuie s dein cunotine detaliate cu privire la dezvoltarea software.
Aceti utilizatori sunt programatori de aplicaii, care cunosc limbajul de
programare folosit la elaborarea aplicaiei i modul n care este ea realizat. n
schimb, nu este necesar s cunoasc n detaliu cum funcioneaz sistemul (doar
dac nu cumva sunt implicai n dezvoltarea de drivere pentru dispozitive hardware
sau de compilatoare, asambloare, etc.).
Ambele niveluri, 4 i 5, sunt independente de sistem, adic de tipul
particular de procesor folosit. O aplicaie scris n limbaj de nivel nalt poate fi
rulat de sisteme cu arhitecturi diferite fr modificri suplimentare aduse codul
surs. Aceast caracteristic a aplicaiei se numete portabilitate, i tot ce trebuie
fcut este ca programul s fie recompilat cu un compilator nativ sistemului
respectiv. n fapt, de aici provine i denumirea de limbaj de nivel nalt - denot
capacitatea limbajului de programare de a oferi o abstractizare a arhitecturii
sistemului de calcul. Unele limbaje pot ascunde complet detaliile specifice unei
anumite arhitecturi: modul de adresare a memoriei, alocarea/dealocarea memoriei,
lucrul cu dispozitivele externe, etc..
n contrast, dezvoltarea de programe sub nivelul 4 este dependent de
sistem. Limbajul de asamblare se numete i limbaj de programare de nivel sczut
deoarece necesit cunotine detaliate cu privire la organizarea intern a sistemului:
arhitectura procesorului, organizarea memoriei, .a.m.d. Instruciunile limbajului
de asamblare sunt native procesorului. Un program scris n limbaj de asamblare
pentru procesoare Intel nu poate fi executat de un procesor ARM sau SPARC.
Limbajul main este o rud apropiat a limbajului de asamblare. Tipic,
este o coresponden de unu la unu ntre instruciunile limbajului de asamblare i
instruciunile main. Procesorul nelege numai limbajul main, ale crui
instruciuni consist din iruri de 0 i 1.
Vom vedea c, dei limbajul de asamblare este considerat de nivel sczut,
programarea n asamblare nu expune toate mruntaiele sistemului. Unele detalii
sunt ascunse de sistemul de operare. De exemplu, citirea datelor de la tastatur sau
afiarea lor la monitor se face prin intermediul unor funcii puse la dispoziie de
sistemul de operare.

Nivelul hardware execut instruciunile limbajului main.

3.1. Tipuri de limbaje de programare


3.1.1. Limbajul main
Fiecare familie de procesoare (x86, SPARC, Power, MIPS) are propriul
su limbaj, denumit cod main, exprimat n idiom binar. Acest limbaj este definit
n etapa de proiectare i cuprinde totalitatea instruciunilor pe care le poate executa
un procesor. Instruciunea binar definit de productor n procesul de realizare a
procesorului se numete intruciune n cod main sau instruciune main. Tipuri
diferite de procesor conin tipuri diferite de instruciuni main. Procesoarele sunt
clasificate frecvent pe baza numrului i tipului de instruciuni main cunoscute.
Un program n cod main este denumit i cod obiect. Figura 3.2 arat un
fragment de cod main scris pentru un procesor cu arhitectur IA-32. S
presupunem c n memoria principal, reprezentat ca ir liniar de octei, avem
secvena de bii prezentat n Figura 3.2.
10110000
10001001
11000010
10001001
11010001
10001001
11001000
7

Figura 3.2 Instruciuni main n memoria principal

n timpul rulrii, procesorul citete instruciuni main din memorie.


Fiecare instruciune main poate conine unul sau mai muli octei de informaie.
Acetia instruiesc procesorul s efectueze un proces specific: operaii aritmetice i
logice, transferul datelor ntre memorie i procesor, etc.. Datele prelucrate de
instruciunile main sunt de asemenea stocate n memorie, iar octeii
instruciunilor main nu sunt diferii de octeii datelor utilizate de instruciune.
Biii de date ilustrai n Figura 3.3 sunt aceeai cu biii de cod prezentai n Figura
3.2, numai semnificaia lor este diferit. Pentru a diferenia datele de instruciuni
sunt utilizai indicatori (pointeri) speciali care ajut procesorul s diferenieze zona
de memorie care stocheaz date de zona de memorie care stocheaz instruciuni.

10110000
10001001
11000010
10001001
11010001
10001001
11001000
7

Figura 3.3 Secven de date n memoria principal

Indicatorul de instruciune ajut procesorul s diferenieze instruciunile


deja executate de cele care urmeaz s fie procesate. Evident, exist instruciuni (de
ex., instruciunea de salt la o anumit adres din program) capabile s schimbe
locaia memorat n indicatorul de instruciune. n mod similar, exist un indicator
de date, care adreseaz zona de memorie n care sunt stocate datele, i un indicator
de stiv, care adreseaz zona de memorie destinat stivei.
Fiecare instruciune main trebuie s conin cel puin un octet denumit
opcode (operation code ), i unul sau mai muli octei de informaie. Opcode-ul,
sau codul de operaie, definete operaia ce trebuie realizat de procesor. Fiecare
familie de procesoare are predefinit propriul set de coduri de operaie care
definete toate funciile disponibile.
S traducem ce reprezint pentru un procesor 80386 codul din zona de
memorie din exemplul nostru:
primii doi octei, 1 0 0 0 1 0 0 1 1 1 0 0 1 0 0 0 (citii normal, de la stnga
la dreapta), foreaz procesorul s copieze n registrul EAX valoarea din
registrul ECX.
urmtorii doi octei, 1 0 0 0 1 0 0 1 1 1 0 1 0 0 0 1, foreaz procesorul s
copieze valoarea lui EDX n ECX.
urmtorii doi octei copiaz valoarea din EAX n EDX.
Toate aceste operaii sunt deduse dac tim c opcode-ul pentru
operaiunea de copiere dintr-un registru n altul este 1 0 0 0 1 0 0 1 1 1, iar codul
pentru registrele EAX, ECX, EDX, este 0 0 0, 0 0 1, respectiv 0 1 0.
Reprezentarea hexazecimal a codurilor instruciune
Programul scris n limbaj main devine mai uor de neles dac se
recurge la reprezentarea codurilor binare n hexazecimal. Se obine un aa numit
hexdump (reprezentare hexazecimal) al programului.
B0
89

C2
89
D1
89
C8
7

Figura 3.4 Reprezentarea instruciunilor main n hexazecimal

3.1.2. Limbajul de asamblare


Codul main este limbajul neles de procesor. Limbajul de asamblare
poate fi definit ca fiind limbajul ce permite programatorului s aib control asupra
codului main. Limbajul de asamblare specific cod main. Instruciunile sale
sunt de fapt reprezentri simbolice ale instruciunilor main. Aceste coduri
simbolice sunt denumite mnemonici.
1 0 0 0 1 0 0 1 1 1 este definit simbolic prin cuvntul MOV, care primete ca
argumente dou registre. Mnemonica MOV nseamn efectuarea unei copieri.
Deoarece sintaxa complet este 1 0 0 0 1 0 0 1 1 1 S S S D D D, unde S S S
este codul pentru registrul surs, iar D D D este codul pentru registrul destinaie, 1
0 0 0 1 0 0 1 1 1 0 0 0 0 0 1 nseamn MOV ECX,EAX.
Secvena din exemplul nostru scris n limbaj de asamblare arat astfel:
mov eax,ecx
mov ecx,edx
mov edx,eax
Procesul prin care se covertete un program din limbaj de asamblare n cod
main se numete asamblare, iar unealta software care convertete un program
scris n limbaj de asamblare n limbaj main se numete asamblor. Alternativ,
procesul de conversie a codului main n limbaj de asamblare se numete
dezasamblare, iar programul care realizeaz acest proces se numete dezasamblor.
Limbajul de asamblare este proiectat pentru o anumit familie de
procesoare. Limbajul de asamblare destinat unui procesor Intel x86 are alt
mnemonic fa de limbajul de asamblare destinat unui procesor SPARC, de
exemplu.

3.1.3. Limbaje de nivel nalt


Putem programa n limbaj main, dar este foarte dificil. Chiar i cel mai
simplu program oblig programatorul s specifice o mulime de coduri de operaie

i octei de date. n plus, programul ar fi rulat numai de tipul de procesor cruia i-a
fost dedicat programul respectiv.
Limbajele de nivel nalt, de ex. C, au fost concepute pentru a nu ine cont
de caracteristicile tehnice specifice unui procesor. Instruciunile limbajului de nivel
nalt pot fi convertite n cod obiect pentru fiecare familie de procesoare n parte.
Totui, codul scris n limbaj de nivel nalt trebuie tradus printr-un mecanism sau
altul n formatul limbajului main. Din acest punct de vedere, programele scrise n
limbaj de nivel nalt pot fi clasificate n trei mari categorii:
compilate
interpretate
hibride
Programe compilate
Majoritatea aplicaiilor sunt create n limbaje compilate. Programatorul
scrie programul folosind sintaxa specific unui limbaj de nivel nalt. Fiierul care
conine programul n format text se numete fiier surs sau cod surs. Acest text
este convertit n cod main specific unui tip de procesor. De obicei, ceea ce se
numete comun compilare este un proces n doi pai:
conversia codului surs n cod obiect. Programul care realizeaz acest pas
se numete compilator.
editarea legturilor ntre diferite module obiect n vederea obinerii
executabilului. Programul care efectueaz acest pas se numete editor de
legturi, sau linker.
Compilator
Fiier surs

Fiier obiect
Editor de
legturi
Alt$fiier$obiect

Fiier$executabil

Biblioteci$de$fiiere$obiect

Figura 3.5 Procesul de compilare

Paii procesului de compilare convertesc instruciunile limbajului de nivel


nalt n instruciuni main. Fiecrei linii de cod scrise n limbaj de nivel nalt i
corespunde una sau mai multe instruciuni main specifice tipului de procesor pe
care va rula aplicaia. De exemplu, codul

int main()
{
int i=1;
exit (0);
}
este compilat n urmtoarele instruciuni main:
55
89
83
C7
83
6A
E8

E5
EC
45
EC
00
D1

08
FC 01 00 00 00
0C
FE FF FF

Acest pas produce un fiier intermediar, numit fiier obiect. Fiierul obiect
conine cod obiect ntr-un anumit format neles de sistemul de operare, dei nc
nu poate fi rulat de acesta. Codul obiect conine numai datele i instruciunile
main ale funciilor definite n program. Programul poate avea ns nevoie de
componente aflate n alte fiiere obiect (de ex., funcia exit). Pentru adugarea
acestor componente este necesar nc un pas. Dup transformarea codului surs n
cod obiect, un editor de legturi leag fiierul obiect al programului de alte
fiiere obiect necesare acestuia i creaz fiierul executabil. Rezultatul editorului de
legturi este un executabil ce poate fi rulat numai de tipul de sistem de operare pe
care a fost compilat programul. Din nefericire, fiecare sistem de operare folosete
un format de fiier executabil (sau obiect) diferit. O aplicaie compilat pe un
sistem de operare tip Windows nu va rula pe Linux, sau viceversa.
Fiierele obiect care conin funcii foarte uzuale pot fi combinate ntr-un
singur fiier, numit bibliotec. Bibliotecile pot fi legate de aplicaii n timpul
compilrii (biblioteci statice) sau n timpul rulrii aplicaiei (biblioteci partajate).
Bibliotecile statice sunt acele biblioteci ale cror module obiect (componente) sunt
incluse n fiierul executabil n momentul editrii de legturi. n cazul bibliotecilor
partajate, modulele obiect sunt adresate n momentul lansrii n execuie sau n
momentul rulrii.
Programe interpretate
Spre deosebire de programul compilat, care ruleaz prin fore proprii,
programul interpretat este citit i rulat de un program separat, numit interpretor.
De-a lungul prelucrrii aplicaiei, interpretorul citete i decodific (interpreteaz)
fiecare instruciune n parte. Conversia programului n instruciuni main specifice

procesorului este realizat de interpretor n timpul rulrii programului. Evident,


punctul sensibil al acestor tipuri de programe este viteza. n loc ca aplicaia s fie
compilat direct n cod main, un program intermediar citete fiecare linie de cod
surs i proceseaz operaiile respective. La timpul de execuie se adaug timpul de
care are nevoie interpretorul s citeasc i s decodifice instruciunile aplicaiei.
Avantajul este conveniena. Pentru programele compilate, la fiecare
modificare a programului, fiierul surs trebuie recompilat. La programele
interpretate, fiierul surs poate fi modificat rapid i uor chiar n timp ce
programul ruleaz. n plus, programul interpretor determin automat funciile
adiionale necesare codului principal (codul obiect al sursei iniiale).
Programe hibride
Programele hibride combin caracteristicile programelor compilate cu
versatilitatea programelor interpretate. Un exemplu perfect este limbajul Java.
Java este compilat n aa numitul cod octet (byte code). Codul octet este
similar codului main rulat de procesor, dar el nsui nu este compatibil cu nicio
familie de procesoare. n schimb, codul octet Java trebuie interpretat de o main
virtual (JVM - Java Virtual Machine) care ruleaz separat. Codul octet Java este
portabil, n sensul c poate fi rulat de ctre orice JVM, pe orice tip de sistem de
operare sau procesor. Implementrile mainilor virtuale Java pot diferi de la
platform la platform, dar toate pot interpreta acelai cod octet Java fr a fi
necesar recompilarea sursei originare.

3.2. Procesul de compilare


n concluzie, un program este compus din una sau mai multe instruciuni ce
descriu un proces, sau un set de procese, realizat de calculator, iar codul surs este
o secven de instruciuni i/sau declaraii scrise ntr-un limbaj de programare
lizibil. Codul surs poate fi convertit n cod executabil de ctre un compilator sau
poate fi executat n timpul rulrii cu ajutorul unui interpretor. Executabilul este un
fiier al crui coninut este interpretat drept program de ctre sistemul de operare.
Dei un fiier sub form de cod surs poate fi un executabil (acest tip de fiier se
numete script), majoritatea fiierelor executabile conin reprezentarea binar a
instruciunilor main (de aceea se mai numete i binar) specifice unui tip de
procesor.
Compilatorul este un program (sau un set de programe) care translateaz
textul scris n limbaj de programare de nivel nalt n limbaj main. Programul
originar este numit cod surs, iar programul obinut este numit cod obiect.
Portabilitatea sistemului de operare Unix a fost n parte consecina faptului

c n 1973 a fost rescris ntr-un limbaj de nivel nalt, i anume, C. GCC (GNU C
Compiler), prima versiune nonproprietar de compilator C, a fost scris de Richard
Stallman n 1989 pentru proiectul GNU. Numele su, GNU, care provine de la
GNU's Not Unix, proclam independena de restriciile impuse de drepturile de
copiere. Compilatorul GNU C este foarte folosit, nu numai deoarece este gratuit,
dar i pentru c a impus un standard n ceea ce privete utilitatea.
GCC translateaz un program scris n C n cod main. Realizeaz n
acelai timp cei doi pai ai procesului de compilare, adic compilarea propriu zis
i editarea de legturi. Rezultatul este un executabil ce poate fi stocat n memoria
calculatorului i rulat de procesor. Compilatorul GNU C lucreaz n etape, aa cum
se poate observa din Figura 3.6.
Cod surs

Unitate de
translatare

Cod n
asamblare

Cod obiect

Fiier
executabil

prog.c

prog.i

prog.s

prog.o

a.out

Preprocesare

Compilare

Asamblare

Editarea de legturi

gcc -E
gcc -S
gcc -c
gcc

Figura 3.6 Etapele procesului de compilare

Comanda gcc prog.c parcurge toate etapele necesare translatrii


codului surs i genereaz un executabil, numit a.out, capabil s ruleze exclusiv
pe sisteme Linux. Pentru a stopa execuia procesului la un anumit moment, putem
folosi opiunile din reprezentarea de mai sus.
Pentru exemplificare, considerm programul C de mai jos. Nu este necesar
s cunoatei limbajul C. Ne intereseaz numai s nelegem efectul etapelor
parcurse de compilator.
/* prog.c */
#include <stdio.h>
int main() {
int var1 = 40;
int var2 = 50;
int var3;
var3 = var1;
var1 = var2;
var2 = var3;

return 0;
}

3.2.1. Preprocesarea
Etapa de preprocesare presupune nlocuirea/expandarea directivelor de
preprocesare din fiierul surs.
Preprocesorul utilizat de GCC n aceast etap este un program de sine
stttor numit CPP. Preprocesorul CPP analizeaz macrourile (cuvintele din surs
care ncep cu caracterul #) i le expandeaz. n cazul nostru, directiva #include
copiaz coninutul fiierului stdio.h n fiierul surs prog.c. Astfel, rezultatul
preprocesrii este tot un program surs, dar care nu mai include directivele
preprocesor, ci rezultatul acestora.
gcc -E prog.c -o prog.i
sau

cpp prog.c prog.i

Din aceste dou variante, prima permite rularea preprocesorului cu toate


opiunile implicite (dezirabil). Fiierul rezultat .i este generat rar, dar poate fi
interesant de vzut cum sunt expandate macrourile complexe. Opiunea -o
specific numele fiierul de ieire.

3.2.2. Compilarea
Compilarea este etapa n care din fiierul preprocesat se obine un fiier n
limbaj de asamblare.
Rezultatul compilrii este un fiier lizibil, cu sufixul .s, coninnd
programul scris n limbaj de asamblare. Compilatorul propriu-zis folosit de GCC
este CC. CC este un program complex, deoarece nu exist o coresponden unu la
unu ntre instruciunile C i instruciunile n asamblare. n plus, poate optimiza
codul n funcie de cerine precum genereaz varianta mai rapid sau genereaz
varianta mai compact, rezultnd secvene diferite de instruciuni n asamblare.
Totui, niciun compilator nu este infailibil, optimizri complexe nu pot fi realizate
dect de un programator n asamblare.
gcc O0 -S prog.c -o prog.s m32
sau
cc -S prog.c -o prog.s

Redm integral rezultatul acestei comenzi:


.file "prog.c"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
subl $16, %esp
movl $40, -4(%ebp)
movl $50, -8(%ebp)
movl -4(%ebp), %eax
movl %eax, -12(%ebp)
movl -8(%ebp), %eax
movl %eax, -4(%ebp)
movl -12(%ebp), %eax
movl %eax, -8(%ebp)
movl $0, %eax
leave
ret
.size main, .-main
.ident
"GCC:
(Ubuntu/Linaro
4.5.2-8ubuntu4)
4.5.2"
.section
.note.GNU-stack,"",@progbits
Aa arat n asamblare programul C din exemplul nostru. Totui, el nu se
regsete n forma studiat de noi n aceast carte. Dei am fi putut genera cu
uurin varianta dorit (gcc O0 -S prog.c -o prog.s m32
mintel)am preferat s folosim setarea implicit. Acest lucru ne permite s
demonstrm faptul c limbajul de asamblare nu este un limbaj de sine stttor, ci
mai degrab o familie de limbaje.
Limbajul de asamblare pentru arhitecturi Intel nu are o singur sintax. n
fapt, sunt dou mari familii: sintaxa Intel i sintaxa AT&T. Toate sistemele de
operare derivate din Unix sau nrudite folosesc nativ sintaxa AT&T. Toate
sistemele cu origini n DOS i Windows folosesc sintaxa Intel. Sintaxa Intel este
forma limbajului de asamblare specificat n documentaia Intel. Cele dou sintaxe
nu sunt foarte diferite, folosirea uneia sau alteia fiind mai degrab o problem de
gust. Pe un sistem de operare Linux sintaxa implicit este AT&T, dar putem folosi
oricare din cele dou. Dispunem i de un utilitar capabil s transforme un program
dintr-o sintax n alta sau dintr-un idiom specific unui asamblor n altul (deoarece

asambloarele nsi introduc modificri de sintax).


intel2gas -g prog.s -o intel.asm
Rezultat:
;FILE "prog.c"
SECTION .text
GLOBAL main
GLOBAL main:function
main:
push
ebp
mov
ebp,esp
sub
esp,16
mov
dword [ebp-4],40
mov
dword [ebp-8],50
mov
eax, [ebp-4]
mov
[ebp-12],eax
mov
eax, [ebp-8]
mov
[ebp-4],eax
mov
eax, [ebp-12]
mov
[ebp-8],eax
mov
eax,0
leave
ret
GLOBAL main:function (.-main)
;IDENT
"GCC:
(Ubuntu/Linaro
4.5.2-8ubuntu4)
4.5.2"

3.2.3. Asamblarea
Etapa de asamblare translateaz codul scris n limbaj de asamblare n cod
binar.
Asamblorul utilizat de GCC se numete AS. AS este un asamblor care
cunoate sintaxa AT&T. Rezultatul asamblrii este un fiier obiect. Fiierul obiect
reprezint un amestec de cod main i alte informaii necesare n faza de editare
de legturi; conine o list de simboluri (de ex., nume de variabile declarate, dar
nedefinite), locurile n care acestea apar n program, informaii de relocare
(specific adresele de memorie la care vor fi plasate datele i instruciunile
programului n etapa de editare de legturi). Nu n ultimul rnd, fiierele obiect pot
conine informaie de depanare, inclus n corpul lor cu opiunea -g. Aceast
opiune este necesar dac se intenioneaz depanarea programului.

gcc -g -c prog.s -o prog.o


sau
as -g prog.s -o prog.o
Fiierul obiect prog.o este generat ntr-un format binar specific platformelor Linux,
numit ELF (Executable and Linkable Format). Acesta descrie modul n care trebuie
structurat binarul unui program astfel nct s poat fi executat de un sistem de
operare Linux. Sistemele de operare Windows folosesc un format diferit (PE
Portable Executable). De aceea, o aplicaie compilat pentru Linux nu ruleaz pe
Windows, sau invers.
n Linux putem dezasambla coninutul unui fiier binar (obiect sau
executabil) cu ajutorul unui utilitar ca objdump. Dezasamblarea nseamn
recuperarea mnemonicilor limbajului de asamblare pornind de la codul main;
este operaia invers asamblrii.
objdump -d -M intel prog
prog.o:

file format elf32-i386

Disassembly of section .text:


08048394 <main>:
8048394:
55
8048395:
89 e5
8048397:
83 ec 10
804839a:
c7 45 fc 28 00 00 00
80483a1:
c7 45 f8 32 00 00 00
80483a8:
8b 45 fc
80483ab:
89 45 f4
80483ae:
8b 45 f8
80483b1:
89 45 fc
80483b4:
8b 45 f4
80483b7:
89 45 f8
80483ba:
b8 00 00 00 00
80483bf:
c9

push ebp
mov ebp,esp
sub esp,0x10
mov DWORD PTR [ebp-0x4],0x28
mov DWORD PTR [ebp-0x8],0x32
mov eax,DWORD PTR [ebp-0x4]
mov DWORD PTR [ebp-0xc],eax
mov eax,DWORD PTR [ebp-0x8]
mov DWORD PTR [ebp-0x4],eax
mov eax,DWORD PTR [ebp-0xc]
mov DWORD PTR [ebp-0x8],eax
mov eax,0x0
leave

Se observ clar diferena dintre limbajul de asamblare i limbajul main. Limbajul


main, n format hexazecimal, este reprezentat cu bold, iar n dreapta este
programul n limbaj de asamblare (n format Intel). Observai mnemonica
instruciunilor n limbaj de asamblare - abrevieri apropiate limbajului uman (NOP
no operation, MOV move, INT interrupt). Din listing reiese echivalentul n cod

main al instruciunii NOP este 90H, irul binar 1001 0000.


n concluzie, limbajul de asamblare este un limbaj mnemonic - un cod
format din una sau mai multe litere ce reprezint un numr, abrevieri, cuvinte care
uureaz memorarea instruciunilor complexe i fac programarea mai uoar.
Programarea n cod main este posibil, dar predispus la erori i mare
consumatoare de timp. Mnemonica uureaz sarcina programatorului, n schimb,
un program scris n limbaj de asamblare trebuie convertit n cod main. Acest
lucru este realizat de asamblor. Asamblorul genereaz translatri unu la unu ntre
cuvinte mnemonice i instruciuni main.
A nu se confunda asamblorul cu compilatorul. Acesta din urm este folosit
la conversia n cod main a unui program de nivel nalt, conversia efectundu-se
n bloc. De asemenea, convertoarele ce translateaz linie cu linie se numesc
interpretoare.

3.2.4. Editarea legturilor


Un fiier obiect este asociat unui singur fiier surs. Fiierul obiect conine
datele i codul funciilor proprii unui program. Datele i funciile poart numele
generic de simboluri. Simbolurile externe modulului (adic funciile sau datele
nedefinite local, prezente n alte fiiere obiect) sunt marcate ca nedefinite. Aceast
etap este folosit la rezolvarea simbolurilor nedefinite (operaie denumit i
rezolvarea simbolurilor) i la unificarea ntr-un singur fiier a mai multor fiiere
obiect sau biblioteci. Rezultatul este un fiier binar cu un format apropiat de fiierul
obiect. n Linux este chiar acelai format, ELF. Editarea de legturi se face cu
ajutorul programului LD, dar care, n cazul programelor C, e preferabil s nu fie
apelat separat (programul poate include numeroase biblioteci i lista fiierelor
obiect poate fi foarte mare). n cazul programelor scrise n asamblare va fi folosit
de fiecare dat.

3.2.5. Formatul fiierelor executabile


Un fiier executabil este un fiier binar obinut dintr-un set de fiiere obiect
i biblioteci n urma operaiei de editare de legturi.
Un fiier executabil conine codul main necesar ndeplinirii operaiilor,
dar i antete i seciuni de formatare auxiliare care specific organizarea
executabilului n memorie, modul n care se folosete codul, zonele de memorie
alocate pentru acesta i date, etc.. Un fiier executabil are, aadar, un format bine
definit i totodat strns legat de sistemul de operare. Sistemul de operare este
responsabil cu interpretarea fiierului executabil i generarea unui proces pe baza
acestuia.

obiect
sunt:

Reinem c formatul de fiier executabil este acelai i pentru fiierele


sau bibliotecile partajate. Exemple de formate de fiiere obiect/executabile
a.out primul format folosit de sistemele Unix;
ELF folosit n sistemele Unix;
PE formatul implicit pe sistemele Windows;
Mach-O formatul implicit pe Mac OS X.

3.3. Avantajele limbajului de asamblare

Avantajele limbajelor de nivel nalt:


Permit dezvoltarea rapid a programelor. Limbajele de nivel nalt ofer
funcii, proceduri, structuri de date predefinite care faciliteaz dezvoltarea
rapid a programelor. n comparaie cu echivalentele lor n limbaj de
asamblare, programele n limbaj de nivel nalt au numr relativ mic de linii
de cod i sunt uor de scris.
Programele sunt uor de revizuit. Programele scrise n limbaj de nivel nalt
sunt mai uor de neles i, dac respect anumite metode de programare,
uor de revizuit. Putem aduga cod sau corecta erori de programare foarte
rapid.
Programe portabile. Programele n limbaj de nivel nalt ascund nivelurile
dependente de sistem. Ca rezultat, ele pot fi folosite pe diferite arhitecturi
de sistem cu puine, sau fr, modificri. n contrast, programele n limbaj
de asamblare sunt total dependente de procesor.
Avantajele limbajului de asamblare:
Eficiena. Eficiena se refer la ct de bun este un program n
ndeplinirea unui obiectiv. ntotdeauna cnd judecm calitatea unui
program trebuie s inem cont de cantitatea de memorie ocupat i de
timpul necesar acestuia n ndeplinirea obiectivului pentru care a fost
realizat. Programele scrise n limbaj de asamblare, din perspectiva spaiului
de memorie pe care l necesit fa de echivalentele lor n limbaj de nivel
nalt, sunt mult mai compacte. De asemenea, timpul necesar execuiei
programului n asamblare este mai mic dect echivalentul su de nivel
nalt.
Acces eficient la hardware-ul sistemului. Limbajele de nivel nalt, prin
natura lor, furnizeaz o imagine abstract a hardware-ului pe care l
utilizeaz. Din aceast cauz este aproape imposibil ca programele s poat
executa sarcini care necesit acces direct la hardware-ul sistemului. De

exemplu, scrierea unui driver pentru un scaner aproape sigur necesit


cunotinte de programare n limbaj de asamblare.

3.4. Exerciii
3.1. Ce relaie exist ntre limbajul de asamblare i limbajul main?
3.2. De ce este considerat limbajul de asamblare limbaj de nivel sczut iar C limbaj
de nivel nalt?
3.3. De ce este important portabilitatea? Dac doreti ca un program s fie
portabil, scrii programul n C sau n limbaj de asamblare?
3.4. Care este diferena ntre compilator i asamblor?
3.5. Ce rol are editorul de legturi?
3.7. Ce nseamn dezasamblare?
3.8. Ce conine un fiier obiect?
3.9. Care sunt avantajele programelor interpretate?

4. DEZVOLTAREA PROGRAMELOR N
LIMBAJ DE ASAMBLARE

Acest capitol prezint procesul de dezvoltare a programelor n limbaj de


asamblare. Prima seciune a capitolului descrie elementele constitutive ale
programelor n asamblare i modul n care acestea definesc un cadru de baz (un
arhetip). Se continu cu definirea noiunii de proces i prezentarea structurii
acestuia n memoria principal. Urmeaz o seciune esenial programrii n
limbaj de asamblare, i anume declararea datelor n memorie. Pe baza tuturor
acestor noiuni sunt prezentate uneltele de dezvoltare necesare crerii
programelor n limbaj de asamblare.

4.1. Sintaxa limbajului de asamblare


Programele scrise n limbaj de asamblare sunt formate din trei categorii
diferite de declaraii.
Prima categorie spune procesorului ce s fac. Aceste declaraii sunt
numite instruciuni executabile sau simplu, instruciuni. Fiecare instruciune
conine un cod de operaie ce comand asamblorului generarea unei instruciuni n
cod main. De obicei, fiecare instruciune executabil genereaz o instruciune n
cod main.
A doua categorie furnizeaz asamblorului informaii cu privire la diferite
aspecte legate de procesul de asamblare. Aceste instruciuni se numesc directive de
asamblare sau pseudoinstruciuni. Pseudoinstruciunile nu genereaz instruciuni n
cod main.
Ultima clas de declaraii pune la dispoziia programatorului un mecanism
de substituie a textului, numit macrodefiniie (macro).
Toate declaraiile limbajului de asamblare sunt scrise linie cu linie n
fiierul surs i toate au acelai format:
[etichet] operaie [operanzi] [;comentariu]
n unele declaraii, cmpurile scrise ntre parantezele ptrate sunt opionale.
Cmpul etichet reprezint un nume (litere, cifre sau caractere

speciale), primul caracter fiind liter sau caracter special. Fiecare etichet are
asociat o valoare - adresa relativ a liniei respective n cadrul segmentului.
Cmpul operaie reprezint mnemonica instruciunii.
Cmpul operanzi definete operandul sau operanzii asociai
instruciunii, conform sintaxei cerute de codul de operaie. Pot fi constante,
simboluri sau expresii de simboluri.
Cmpul comentariu reprezint un text oarecare precedat de caracterul
punct i virgul.
Cmpurile unei declaraii trebuie separate printr-un spaiu sau un caracter
TAB. Numrul acestor caractere despritoare rmne la discreia programatorului.
Dei asamblorul ignor tot ce depete un singur caracter TAB, utilizarea mai
multor caractere de acest fel permite evidenierea structurii programului i d
claritate textului.

4.2. Structura programului


Un program, indiferent de limbajul folosit la scrierea sa, este alctuit din
dou pri importante: cod i date. Codul, tradus n limbaj main, este plasat ntr-o
zon de memorie numit segment de cod, iar datele, n funcie de tipul lor, n zone
de memorie numite segmente de date. mprirea programelor scrise n limbaj de
nivel nalt n segmente are loc n procesul de compilare. n cazul programelor
scrise n limbaj de asamblare segmentele sunt vizibile nc din fiierul surs.
Programatorul n asamblare este obligat s specifice prin directive speciale
nceputul fiecrui segment folosit de programul su i s l populeze n
consecin. Denumirile exacte depind de asamblor. NASM (Netwide Assembler),
MASM (Microsoft Assembler), TASM (Borland Turbo Assembler) sunt cteva din
cele mai populare asambloare pentru procesoarele Intel. Asamblorul folosit de noi
delimiteaz segmentele prin directivele segment sau section.
Pn n acest capitol am tot repetat faptul c instruciunile i datele unui
program sunt introduse n segmente de memorie separate, aadar, poate c nu v
ateptai s existe mai multe segmente de date. Urmtorul listing prezint structura
tipic a unui program scris n asamblare:
section .rodata ;declar datele protejate la scriere
section .data
;declar segmentul de date iniializate
...
section .bss

;declar segmentul de date neiniializate

...
section .text
global _start
_start:
nop

;declar segmentul de cod

...

;instruciunile programului

;instruciunea No Operation

;instruciuni care ntrerup rularea programului


mov eax,1
mov ebx,0
int 80h
section .rodata
date read-only
section .data
date iniializate

section .bss
date neiniializate

section .text
cod

Figura 4.1 Structura programului n limbaj de asamblare

Datele programelor sunt de dou tipuri: iniializate i neiniializate.


Section .data declar elemente de date ce dispun de valoare iniial;
adic elementele de date crora la momentul rezervrii spaiului de memorie li se

specific i valoarea iniial (valoarea introdus n locaia de memorie respectiv).


Aceste elemente de date sunt folosite n programele de nivel nalt ca variabile
globale sau statice, iniializate.
//variabil global iniializat (n C)
int i = 56;
void main() {
}
Variabilele sunt locaii de memorie din care se pot scrie i citi valori. Declararea
variabilei presupune rezervarea unui spaiu de memorie corespunztor pstrrii
unei valori. Adresa la care a fost ncrcat n memorie variabila este reprezentat
chiar de numele variabilei. Dac folosim analogia memoriei ca dulap cu sertare,
declararea unei variabile iniializate nseamn introducerea unei valori n sertar i
etichetarea acestuia cu numele variabilei.
Unele date iniializate sunt protejate, n sensul c nu pot fi rescrise n
timpul rulrii programului. Acestea se gsesc n seciunea declarat cu section
.rodata. Din aceast categorie fac parte literalii din C. De exemplu, n cazul
instruciunii printf (Hello World!\n), irul Hello World\n este
stocat n seciunea .rodata.
n segmentul de date neiniializate, declarat cu section .bss, rezervm
un anumit numr de locaii de memorie crora le asociem o denumire. Etichetm
sertarul dar nu specificm nicio valoare. Valoarea este instaniat automat cu valori
de zero (caracterul NULL). Variabilele neiniializate se folosesc frecvent la
alocarea unor zone de memorie tampon (buffer).
//variabil global neiniializat (n C)
int i;
void main() {
}
Aadar, dac pn n prezent tiam c, n memorie, datele sunt separate de
instruciuni, acum aflm c zona de memorie care conine date iniializate este
separat de zona de memorie care conine date neiniializate. Listingul dinainte de
Figura 4.1 arat modul tipic de dispunere a segmentelor n program. Segmentul
.bss trebuie plasat ntotdeauna nainte de segmentul .text, n timp ce
segmentul .data poate fi plasat i dup. ns trebuie s inem cont c, pe lng
funcionalitate, programul trebuie s fie i lizibil. Gruparea tuturor definiiilor de
date la nceputul fiierului surs faciliteaz nelegerea programului de ctre ali
programatori.
Section .text declar zona de memorie ce conine instruciunile

programului. Ca fapt divers, termenul de program care i modific propriul cod


(self modifying code) denot faptul c programul i poate modifica singur aceast
seciune n timpul rulrii. Pe de alt parte, mult mai important de reinut este faptul
c atunci cnd editorul de legturi convertete fiierul surs n fiier executabil,
acesta trebuie s recunoasc instruciunea din segmentul de text de la care sistemul
de operare trebuie s nceap rularea programului. Programatorul specific punctul
de intrare n program (entry point), declarnd o etichet11, un identificator.
Eticheta _start indic instruciunea de la care trebuie s nceap rularea
programului. Dac editorul de legturi nu gsete aceast etichet, va produce un
mesaj de atenionare (warning: cannot find entry symbol _start;) i va ncerca s
ghiceasc singur punctul de intrare n program. ns nu avem nicio garanie c va
ghici corect.
Nu este obligatoriu ca numele etichetei de intrare n program s fie _start.
n acest caz, n etapa editrii de legturi trebuie s precizm numele punctului de
intrare n program printr-un parametru specific editorului de legturi.
Programatorul poate s anune punctul de intrare n program al aplicaiei
curente i altor programe. Acest lucru se face cu directiva global. Directiva
global indic etichetele din programul curent accesibile altor programe externe.
Segmentul de cod se ncheie cu trei instruciuni. Recunoatem instruciunea
MOV, ntlnit n paragrafele anterioare, i deducem uor efectul: registrul EAX ia
valoarea 1 iar registrul EBX valoarea 0. Instruciunea INT 080H solicit
sistemului de operare s studieze coninutul registrelor i s execute operaia
codificat prin valorile acestora. Codul asociat valorii 1 n registrul EAX cere
sistemului de operare Linux s ntrerup execuia programului. Aa este apelat
funcia de sistem (funcii implementate la nivelul sistemului de operare)
sys_exit ieire din program, n Linux. Vom vorbi pe larg la timpul potrivit,
momentan reinem c aceste linii permit ntreruperea normal a procesului. Un
program aflat n execuie se numete proces.

4.3. Structura procesului


Sistemul de operare, n momentul lansrii programului n execuie i crerii
procesului, aloc memorie pentru segmentele de mai sus. Pe lng aceste zone
definite n executabil, n cadrul unui proces se aloc i dou zone de memorie
11

Denumiri introduse de programator n program cu scopul de a nlesni accesul la zonele de


memorie specificate de acestea.

foarte importante. Acestea sunt zonele de stiv i heap. Pentru a nelege mai bine
structura procesului trebuie s vorbim pe scurt de modul n care sistemul de
operare Linux organizeaz memoria principal n modul protejat de adresare.

4.3.1. Memoria virtual n Linux


n modul protejat, procesoarele Intel x86 sunt procesoare de memorie
virtual. Instruciunile nu specific locaiile de memorie prin adresele lor fizice, ci
prin adrese virtuale (reinem aadar c adresa prezent n corpul unei instruciuni
este o adres virtual). Adresa virtual este un numr alternativ dat unei locaii
fizice de memorie. Sistemul de operare mapeaz adresele virtuale la adrese fizice.
Prin acest mecanism sistemul de operare poate executa n acelai timp12 mai multe
procese independent unul de altul, astfel nct rularea defectuoas a unuia s nu
afecteze rularea celorlalte. Niciun proces nu este contient de faptul c ruleaz
alturi de altele i nici nu poate interfera cu ele. Mecanismul memoriei virtuale
permite sistemului de operare s aloce o zon din spaiul de adrese fizice i s o
prezinte procesului ca ncepnd de la adresa virtual 08048000H. Sistemul de
operare face acest lucru pentru toate procesele. Toate procesele consider c blocul
de memorie alocat lor de ctre sistemul de operare ncepe de la adresa 08048000H
i sfrete aproape de 0BFFFFFFFH (adresa de sfrit nu e ntotdeauna aceeai).
Fiecare proces crede c ruleaz n propria zon de memorie i totui toate zonele
de memorie ale proceselor ncep i sfresc la aceleai adrese. Sistemul de operare
realizeaz acest lucru mapnd aceeai adres virtual la o adres fizic diferit.
Adresa 0BFFFFFFFH nseamn puin peste 3 GB. Chiar dac majoritatea
calculatoarelor au mai puin memorie fizic, sistemul de operare poate promite
programului 3 GB de memorie prin faptul c nu mapeaz toate adresele virtuale la
adrese fizice. De fapt, spaiul de memorie virtual pus la dispoziia procesului este
mprit n dou blocuri:
Blocul inferior - ncepe de la adresa 08048000H i conine seciunile de
cod, date iniializate i neiniializate. Acestuia i este alocat numai spaiul
necesar, n funcie de mrimea codului i a datelor definite.
Blocul superior - ncepe din partea opus, dinspre captul superior al
memoriei, i se ntinde nspre blocul inferior. Adresa de nceput pentru al
doilea bloc nu este ntotdeauna aceeai, dar nu poate fi mai mare de
0BFFFFFFFH. Acest bloc reprezint stiva programului.
Stiva reprezint o zon de memorie n care sunt stocate variabilele locale i
12

Proprietatea sistemului de operare de a executa mai mult de un program simultan se


numete multitasking. Se bazeaz pe capacitatea procesorului de a comuta rapid ntre
procese, crend iluzia de simultaneitate.

parametrii funciilor. La apelul unei funcii, informaiile menionate formeaz un


cadru de stiv (stack frame). Atunci cnd se revine din funcie, cadrul de stiv
asociat este eliberat. n stiv, datele sunt stocate utiliznd metoda ultimul intrat,
primul ieit (Last In First Out). Acest lucru nseamn c spaiul este alocat i
dealocat la un singur capt al memoriei, numit vrful stivei.
Stiva este o seciune din memoria principal utilizat pentru stocarea temporar a
informaiilor, n care cel mai recent element introdus este primul extras.
4 GB

0FFFFFFFFH

KERNEL SPACE
3 GB

0BFFFFFFFH
stiv

USER SPACE

Bloc superior

Acest spaiu de memorie


virtual este alocat numai
la cerere. Nu e gol, ci
NU EXIST
(HEAP)

segment .bss

Bloc inferior

segment .data
segment .text

08048000H
00000000H

Figura 4.2 Aspectul memoriei pentru un proces n linux

Spaiul aparent neutilizat dintre cele dou blocuri nu exist. Dac


programul are nevoie de memorie adiional din acea zon, trebuie numai s
ncerce s o acceseze i sistemul de operare va mapa rapid adresa virtual la o
adres fizic. Aceast zon se numete heap.
Heap denot o zon de memorie folosit pentru crearea i distrugerea
structurilor de date care au timp de via limitat. n acest caz, blocurile de memorie
sunt alocate i eliberate dinamic. Modelul de alocare i dimensiunea blocului de

memorie nu sunt cunoscute dect la momentul rulrii (runtime). Zona heap este
partajat de toate bibliotecile i modulele ncrcate dinamic de proces. ncepe la
sfritul segmentului de date neiniializate i crete ctre adrese mai mari.
Aadar, n Linux, codul i datele programului ncep de jos, aproape de
08048000H, iar stiva ncepe de sus, aproape de 0BFFFFFFFH.

4.4. Tipuri de date


Segmentele .data i .bss conin directive de date care declar alocarea
unor zone de memorie. Sunt dou tipuri de directive de date:
directive D<x>, pentru date iniializate (D=define)
directive RES<x>, pentru date neiniializate (RES=reserve)
x se refer la dimensiunea datelor i se nlocuiete cu indicatorii din
Tabelul 4.1.
Tabelul 4.1 Dimensiunea tipurilor de date

Unitate
Byte (octet)
word (cuvnt)
double word
quad word
ten bytes

Indicator (x)
B
W
D
Q
T

Dimensiune (octei)
1
2
4
8
10

4.4.1. Date iniializate


Declararea unei zone de memorie cu date iniializate presupune definirea a
trei elemente.
etichet: numele acelei zone de memorie.
D<x>, unde x este litera corespunztoare dimensiunii datelor declarate.
DB
DW
DD
DQ
DT

Define
Define
Define
Define
Define

valoare iniial.

Byte
Word
Doubleword
Quadword
Ten Bytes

;aloc 1 octet
;aloc 2 octei
;aloc 4 octei
;aloc 8 octei
;aloc 10 octei

Este important s nelegem c, spre deosebire de limbajele de nivel nalt,


eticheta din limbajul de asamblare nu face altceva dect s indice locaia datelor n
memorie. Este un indicator, o adres. Nu o valoare, nu un tip de date. Eticheta nu
specific o dimensiune. Dei este tentant s o gndim ca variabil, eticheta este
mult mai limitat: reprezint numai adresa virtual a unui octet aflat undeva n
memorie.
Variabilele limbajelor de nivel nalt sunt nume simbolice asociate cu un
spaiu de stocare de mrime definit i cu o valoare cunoscut sau necunoscut. Pe
parcursul programului, att locaia, ct i valoarea, se pot modifica. Dac la
momentul declarrii se atribuie o valoare de nceput (se definete variabila), atunci
vorbim de o variabil iniializat. Dac se aloc spaiu fr atribuirea unei valori
iniiale, vorbim de variabil neiniializat.
Constanta reprezint un tip special de variabil a crei valoare nu poate fi
alterat n timpul execuiei programului. Valoarea rmne fix. Valoarea unei
constante este specificat o singur dat.
;
;date iniializate
;
section .data
var db
55h
;octet cu numele var i valoarea 0x55
vector
db
55h
;o succesiune de trei octei
db
56h
db
57h
_char
db
'a'
;caracterul a
cars
db
'hello',12,10,'$'
;ir
sir
db
"hello"
_short
dw
1234h
character dw
'a'
chars
dw
'abc'
;caracterele 61h, 62h, 63h
_int
dd
12345678h
opt
dq
0123456789ABCDEFh
zece
dt
0FFAAFFAAFFAAFFAAFFAAh
Fiecare element de date este plasat n memorie n ordinea n care este
definit. Elementele cu mai multe valori sunt plasate n memorie exact aa cum apar
n corpul directivei de date.
Deoarece pentru multe asambloare, inclusiv pentru cel folosit de noi, short
i int sunt cuvinte rezervate (instruciuni), pentru a le folosi ca etichete am adugat
prefixul underscore. Am reprezentat harta memoriei n Figura 4.3.
Cnd utilizm definiii multiple de date, asamblorul aloc un spaiu
contiguu de memorie. De aceea, secvena de directive vector poate fi abreviat:

vector

db

55h,56h,57h

Din acest punct de vedere, secvena,


cars

db

'hello',12,10,'$'

este abrevierea de la
cars

db
db
db
db
db
db
db
db

'h'
'e'
'l'
'l'
'o'
12
10
'$'

Valorile de iniializare pot fi definite i ca expresii. De exemplu,


_init

dw

10*25

este echivalent cu
_init

dw

250

Asamblorul evalueaz expresia i atribuie etichetei valoarea rezultat. Dei


utilizarea unor astfel de expresii nu este indicat, exist situaii n care o expresie
evideniaz mai bine semnificaia datei respective.
Ordinea octeilor
Un octet poate reprezenta o valoare cuprins ntre 0 i 255. Numerele mai
mari de 255 folosesc mai mult de un octet. O secven de doi octei alturai poate
reprezenta orice numr ntre 0 i 65535. Totui, o dat ce ai o valoare numeric
care nu poate fi reprezentat pe un singur octet, ordinea octeilor devine crucial.
Lum ca exemplu numrul zecimal 681. Limbile europene evalueaz
numerele de la dreapta la stnga. Numrul 681 const n 6 sute, 8 de zece i 1 de
unu, nu din 1 sute, 8 de zece i 6 de unu. Prin convenie, cea mai puin
semnificativ coloan este cea din dreapta i valorile cresc de la dreapta la stnga.
Dac transformm 681 n hexazecimal obinem o secven de doi octei: 02A9H.
Cel mai semnificativ octet (MSB Most Significant Byte) este cel din stnga

(02H), iar cel mai puin semnificativ (LSB Least Significant Byte) este cel din
dreapta (A9H). Dac am inversa ordinea lor am obine un numr zecimal diferit
(A902H = 43266). De aceea, trebuie s fim ateni cum scriem valorile zecimale
reprezentate n hexazecimal i, mai ales, s tim cum judec sistemul de calcul
numerele hexazecimale. Din acest ultim punct de vedere, sistemul de calcul are
dou posibiliti:
octetul mai puin semnificativ este introdus la adres de memorie mai
mic, iar octetul mai semnificativ la adres de memorie mai mare;
octetul mai puin semnificativ este introdus la adres de memorie mai
mare, iar octetul mai semnificativ la adres de memorie mai mic.
Posibilitile se exclud reciproc. Despre procesorul care stocheaz octetul
cel mai puin semnificativ la adres mai mic i octetul cel mai semnificativ la
adres mai mare spunem c respect convenia little-endian. Procesorul care
stocheaz octetul cel mai semnificativ la adres mai mic, lucreaz conform
conveniei big-endian. Am spus procesorul, nu sistemul de operare.
I always regret that I didn't fix up some idiosyncrasies of the 8080 when I had a
chance. For example, the 8080 stores the low-order byte of a 16-bit value before
the high-order byte. The reason for that goes back to the 8008, which did it that
way to mimic the behavior of a bit-serial processor designed by Datapoint (a bitserial processor needs to see the least significant bits first so that it can correctly
handle carries when doing additions). Now there was no reason for me to continue
this idiocy, except for some obsessive desire to maintain strict 8080 compatibility.
But if I had made the break with the past and stored the bytes more logically,
nobody would have objected. And today we wouldn't be dealing with issues
involving big-endian and little-endian - the concepts just wouldn't exist.
Stephen Morse
Sistemul de operare Linux utilizeaz ambele tipuri de ordine, n funcie de
arhitectura hardware pe care este instalat. ntreaga arhitectur x86, de la 8086 pn
la Haswell, este little-endian. Alte arhitecturi hardware, ca ARM sau POWER, sunt
big-endian. Unele arhitecturile hardware, MIPS i Intel Itanium, sunt bi-endian, n
sensul c pot fi configurate s interpreteze valorile numerice ori ntr-un fel, ori n
cellalt. Dar asta nu e tot. Limbile europene evalueaz numerele de la dreapta la
stnga, dar irurile de caractere sunt evaluate invers, de la stnga la dreapta. Din
aceast perspectiv, dac un ir arbitrar de digii hexazecimali, ABCD, este
considerat ir de cifre hexazecimale (numr), de exemplu 0ABCDH, atunci MSB
este ABH i LSB este CDH. n memorie, va aprea n ordinea CD AB. Dac un ir
arbitrar de digii hexazecimali, ABCD, este considerat ir de caractere (cuvnt,
text), de exemplu 'ABCD', atunci MSB este caracterul D, iar LSB caracterul A. n

memoria unui sistem little-endian irul de caractere va fi ordonat A B C D.


0xFF
0xAA
0xFF
0xAA
0xFF
0xAA
0xFF
0xAA
0x01
0x23
0x45
0x67
0x89
0xAB
0xCD
0xEF
0x12
0x34
0x56
0x78
0x00
0x63
0x62
0x61
0x00
0x61
0x12
0x34
0x6F
0x6C
0x6C
0x65
0x68
0x24
0x0A
0x0C
0x6F
0x6C
0x6C
0x65
0x68
0x61
0x57
0x56
0x55
0x55

_int este numr. Octetul mai puin


semnificativ, 0x78, la adres mai
mic (little-endian)

Caracterul 'a' reprezentat pe un


cuvnt.
sir este ir de caractere. Octetul mai
puin semnificativ, 'h', la adres mai
mic (little-endian).

caracterul $
10
12
'o'
'l'
'l'
'e'
'h'
'a'

Etichet

zece

opt

_int

chars
character
_short

sir

cars
char
vector
var
0

Figura 4.3 Harta memoriei

Adres
0x80490bb
0x80490ba
0x80490b9
0x80490b8
0x80490b7
0x80490b6
0x80490b5
0x80490b4
0x80490b3
0x80490b2
0x80490b1
0x80490b0
0x80490af
0x80490ae
0x80490ad
0x80490ac
0x80490ab
0x80490aa
0x80490a9
0x80490a8
0x80490a7
0x80490a6
0x80490a5
0x80490a4
0x80490a3
0x80490a2
0x80490a1
0x80490a0
0x804909f
0x804909e
0x804909d
0x804909c
0x804909b
0x804909a
0x8049099
0x8049098
0x8049097
0x8049096
0x8049095
0x8049094
0x8049093
0x8049092
0x8049091
0x8049090

Harta memoriei din Figura 4.1 precizeaz i adresele corespunztoare


etichetelor. Numele simbolic var este adresa 0x8049090, .a.m.d..
Pseudoinstruciunile DB, DW, DD, DQ, DT aloc spaiu de stocare i asociaz
acestuia eticheta prin care poate fi accesat. La momentul asamblrii, asamblorul
atribuie fiecrei etichete un deplasament. Aa cum am specificat anterior,
asamblorul aloc datelor un spaiu contiguu. De asemenea, asamblorul pstreaz
ordinea datelor din fiierul surs. Ca s deduc deplasamentul unui element de
date, asamblorul nu trebuie dect s numere octeii alocai datelor de dinainte. De
exemplu, deplasamentul lui char este 4 deoarece var i vector au alocai 1,
respectiv 3 octei.
Tabelul 4.2 Tabela de simboluri

Nume
var
vector
char
cars
sir
_short
character
chars
_int
opt
zece

Deplasament
0
1
4
5
13
18
20
22
26
30
38

Tabelul 4.2 prezint tabela de simboluri corespunztoare hrii de memorie din


Figura 4.3. Odat aleas adresa de nceput, celelalte sunt calculate prin adunarea
deplasamentului. Acest lucru permite adresarea oricrui element din segmentul de
date prin intermediul etichetei var.
Constante
Constanta este o etichet care are asociat o valoarea fix. O astfel de
etichet este tratat asemntor constantelor din limbajele de nivel nalt. De fiecare
dat cnd asamblorul ntlnete o astfel de etichet, va nlocui numele cu valoarea.
Reinem c acest lucru se ntmpl la asamblare. Constantele pot fi definite cu
directivele EQU, %assign i %define.
Directiva EQU definete constante numerice i nu permite redefinirea
ulterioar n cadrul programului. EQU leag un nume de un operand (de un singur
operand). Valoarea rmne aceeai pe parcursul ntregului program.
De exemplu, urmtoarea directiv definete o constant numit CR.

Valoarea ASCII pentru carriage-return este atribuit acestei constante prin


directiva EQU.
CR
NR_CARACTERE
NR_DE_RAND
NR_DE_COL
MARIME_VECTOR

equ
equ
equ
equ
equ

0Dh ;caracterul carriage-return


16
25
80
NUM_DE_RAND * NUM_DE_COL

Constantele CR, NR_CARACTERE,NR_DE_RAND, etc., nu pot fi


redefinite mai trziu n cadrul programului. Totui, definirea unor constante aduce
programului dou avantaje: devine mai lizibil i mai uor de modificat. Prin
asocierea valoare - nume descriptiv, instruciunea devine mai uor de neles.
Acum tim c valoarea 0DH din acea instruciune reprezint caracterul carriagereturn. Pe de alt parte, apariii multiple ale constantei pot fi modificate dintr-un
singur loc. Pentru a modifica numrul de caractere acceptat de la tastatur de la 16
la 160 trebuie s schimbm numai valoarea din cadrul directivei NR_CARACTERE.
Operandul unei directive EQU poate fi o expresie evaluat la momentul
asamblrii. Ultimele trei propoziii definesc mrimea vectorului la 2000.
Foarte frecvent, constanta definit cu EQU se folosete la memorarea
lungimii unui ir.
mesaj
lungime

db
equ

'Hello world'
$-mesaj

Constanta lungime va avea valoarea 11 i nu va putea fi redefinit pe tot


parcursul programului. Caracterul $ reprezint un simbol special al asamblorului,
evaluat ca fiind adresa de nceput a liniei care conine expresia. Valoarea sa
reprezint deplasamentul curent n segment. Aadar, n cazul exemplului anterior,
caracterul $ indic primul octet dup irul 'Hello world'. Cum mesaj este adresa
de nceput a irului Hello world (adresa lui 'H'), $-mesaj reprezint numrul de
octei ocupai de ir. n cazul de fa, numrul de octei ocupai de ir se confund
cu numrul de elemente al irului, adic lungimea acestuia. Dac considerm irul
mesaj2, lungimea se afl mprind numrul de octei la 4, deoarece fiecare
caracter este reprezentat pe 32 de bii, adic 4 octei.
mesaj2
octeti
lungime2

dd
equ
equ

'Hello world'
$-mesaj2
($-mesaj2)/4

n cazul EQU, simbolurile crora le-au fost alocate o valoare nu pot lua alte

valori pe parcursul programului. Dac sunt necesare redefiniri, trebuie s folosim


directiva %assign. Directiva %assign definete tot o constant numeric, dar
permite redefinirea ulterioar n cadrul programului. De exemplu, definim i ca
fiind j+1 astfel:
%assign

j+1

i mai trziu, n cadrul programului, putem redefini i la valoarea j+10:


%assign

j+10

Directiva %assign este sensibil la majuscule. Simbolurile I i i sunt


tratate distinct. Dac este un efect nedorit se poate folosi %iassign.
Directivele EQU i %assign definesc constante numerice. Directiva
%define poate defini att constante numerice ct i constante tip ir. La fel ca
directiva precedent, %define permite redefinirea i are variant insensibil la
majuscule, %idefine.
%define

[EBX+2]

4.4.2. Date neiniializate


n segmentul datelor neiniializate nu declarm valori, ci rezervm un
anumit numr de locaii de memorie. Fiecare directiv de rezervare primete ca
argument un singur operand - numrul de uniti de memorie care trebuie rezervat
(octei, cuvinte, dublu cuvinte, etc.). Directiva de rezervare RES<x> ia urmtoarele
forme:
RESB
RESW
RESD
RESQ
REST

(REServe a Byte)
(REServe a Word)
(REServe a Doubleword)
(REServe a Quadword)
(REServe Ten Bytes)

;rezerv un octet
;rezerv un cuvnt
;rezerv un dublu cuvnt
;rezerv un cuvnt cvintuplu
;rezerv 10 octei

Aadar, RES<x> specific ntotdeauna numrul de elemente de memorie.


Cteva exemple:
tampon:
cuvnt:

resb 64
resw 1

;rezerv 64 de octei
;rezerv un cuvnt (2 octei)

vector:

resq 10

;rezerv un ir de 10 numere reale (80 octei)

Am putea presupune c declaraiile precedente, introduse n seciunea de


date neiniializate a unui program, cresc mrimea executabilului cu 148 de octei.
Dar un beneficiu al declarrii elementelor n seciunea datelor neiniializate este c
acestea nu sunt incluse n programul executabil.
Datele iniializate sunt incluse n corpul programului executabil deoarece
trebuie iniializate cu o valoare specific. Zonele de date declarate n segmentul
BSS nu sunt iniializate cu valori la creare (sunt iniializate la momentul rulrii),
aadar nu sunt incluse n fiierul surs. n capitolul urmtor, cnd vom prezenta
operatorul TIMES, vom demonstra acest lucru prin testarea mrimii unui program
ce aloc succesiv 100 de elemente de date iniializate i 100 de elemente de date
neiniializate.

4.5. Procesul de dezvoltare al programelor


Privit n mare, procesul de dezvoltare al programelor n asamblare poate fi
rezumat astfel:
1. Editarea fiierului surs;
2. Asamblarea fiierului surs i obinerea fiierului obiect;
3. Coversia fiierului obiect (i a altor module obiect asamblate anterior) ntrun singur fiier executabil cu ajutorul editorului de legturi;
4. Testarea programului prin execuia sa direct sau prin intermediul unui
depanator;
5. Dac algoritmul trebuie mbuntit se revine la pasul 1 i se modific
fiierul surs;
6. Se repet paii anteriori pn la obinerea rezultatului dorit.

4.5.1. Editarea textului


Programele n asamblare (de fapt, orice programe, indiferent de limbajul de
programare utilizat) sunt scrise cu un editor de text. Nu are importan care, dar e
bine ca n alegerea acestuia s inem cont de cteva lucruri. Procesoarele de text
gen Microsoft Word i LibreOffice Writer, pe lng textul propriu zis, includ n
fiiere date suplimentare (de ex., formatarea paginii, tipul de font, mrimea sa,
antete de pagin) de care asamblorul nu are nevoie i pe care nu le nelege. E
posibil ca, n timpul asamblrii, aceast informaie suplimentar s genereze erori.
Pe de alt parte, este dezirabil ca editorul de text s poat evidenia sintaxa

limbajului n care scriem - n cazul nostru, limbajului de asamblare. Nu toate


editoarele de text au aceast proprietate. De aici nainte se presupune c lucrai cu
editorul de text vim.
Vim
Vi este un editor de text prezent n majoritatea sistemelor Unix. Prima
versiune a fost dezvoltat la Universitatea Berkeley n anul 1980. Vim este
acronimul lui Vi Improved, o variant extins a lui vi, creat de Bram
Moolenaar n 1991. Vim include toate caracteristicile vi, plus multe altele noi,
destinate s ajute utilizatorul n procesul de editare al codului surs.
n vim, comenzile sunt introduse numai prin intermediul tastaturii, ceea ce
nseamn c putem ine minile pe tastatur i ochii pe ecran. Pentru utilizarea
acestuia avem nevoie de un terminal, aadar introducei combinaia de taste
CTRL+ALT+T. Cei care nu consider introducerea comenzilor de la tastatur un
avantaj, pot instala gvim, o versiune grafic a lui vim. Gvim asigur integrarea cu
mouse-ul, prezint meniuri i bar de derulare.
Vim este o unealt plin de funcionaliti i dispune de un manual pe
msur. Manualul poate fi activat din interiorul editorului prin comanda :help
(man nu conine foarte multe informaii). n continuare vom prezenta numai
comenzile principale.
Deschiderea unui fiier se realizeaz prin comanda vim fisier.txt.
Dac fiierul nu exista nc, va fi creat. Atenie, creat nseamn c a fost selectat o
zon temporar de memorie (buffer) care va reine textul introdus de noi pn la
salvarea lui pe disc. Dac nchidem fiierul fr s salvm datele pe disc, am
pierdut tot ce am introdus n fiier, inclusiv denumirea. Numai salvarea datelor
duce la crearea fisier.txt pe disc. Din perspectiva interfeei, bufferul este fereastra
n care apare textul n curs de editare. Ecranul vim conine un buffer i o linie de
comand aflat n partea de jos a acestuia. n linia de comand sunt afiate
informaii de stare i pot fi introduse comenzi.
Vim opereaz n mai multe moduri de lucru, ceea ce nseamn c editorul
se comport diferit, n funcie de acestea. n aceast prezentare vom lucra n dou
moduri: comand i editare. n mod comand, tot ce tastm este interpretat de
editor ca o comand. Exemple de astfel de comenzi sunt: salveaz fiierul,
prsete editorul, mut cursorul, terge, caut, nlocuiete, selecteaz poriuni de
text, trece editorul n modul inserare. Modul editare (inserare) ne permite s
introducem text.
Acest dualitate de operare nseamn c orice tast poate reprezenta o
comand sau un caracter. De exemplu, tasta i (insert), introdus n mod comand,
comut editorul n modul editare, n modul editare este pur i simplu caracterul i.
Modul editare este indicat prin cuvntul INSERT afiat pe linia de jos a

terminalului. n zona bufferului, liniile libere sunt indicate prin caracterul ~ (tilda).
n acest moment putem introduce text.
Salvarea fiierului nseamn o comand, aadar, comutm n mod comand
apsnd tasta ESC. Putem iei din editor cu urmtoarele comenzi:
ZZ salveaz bufferul pe disc i iei
:x - salveaz bufferul pe disc i iei (la fel ca ZZ)
:wq salveaz bufferul pe disc i iei (la fel ca ZZ)
:q iei din editor (funcioneaz numai dac modificrile sunt salvate)
:q! - iei din editor fr s salvezi bufferul pe disc
Cu urmtoarele comenzi putem scrie bufferul pe disc.
:w
- salveaz bufferul n fiierul curent (fisier.txt)
:w output.txt - salveaz bufferul n fiierul output.txt; nu rescrie fiierul
n caz c acesta exist
:w! output.txt - salveaz bufferul n fiierul output.txt; rescrie fiierul
dac exist.
Prima comand salveaz modificrile n fiierul dat ca argument. A doua i a treia
comand ne permite s salvm bufferul ntr-un fiier nou. Pentru a afla informaii
suplimentare despre comanda :w, putem folosi :help w. Implicit, ecranul va fi
mprit n jumtate, cu partea superioar afind informaii de ajutor. nchiderea
noii ferestre de face cu :q.
Navigarea prin text se face de regul cu tastele sgei. Dar pentru c este
incomod s mutm mna frecvent n colul din dreapta jos al tastaturii se pot folosi
tastele h, l, k, j (un caracter la stnga, dreapta, sus, jos). n plus, Space
deplaseaz cursorul cu un caracter la dreapta i Enter poziioneaz cursorul la
nceputul liniei urmtoare. Alte comenzi care pot deplasa cursorul sunt:
G
gg
^
$
w
b
fx
Fx
%

- poziioneaz cursorul la ultima linie a documentului


- poziionaz cursorul la prima linie a documentului
- poziioneaz cursorul la nceputul liniei curente
- poziioneaz cursorul la sfritul liniei curente
- poziioneaz cursorul un cuvnt nainte
- poziioneaz cursorul un cuvnt napoi
- sri la urmtoarea apariie a caracterului x pe linia curent
- sri la precedenta apariie a caracterului x pe linia curent
- sri la paranteza care corespunde celei de sub cursor.

De asemenea, n mod comand putem modifica textul:


x
- terge caracterul sub care este poziionat cursorul
X
- terge caracterul dinaintea cursorului
dG
- terge de la linia curent pn la sfritul fiierului
dfx
- terge tot textul ncepnd cu poziia curent pn la urmtoarea
apariei a caracterului x
dd
- terge linia curent
dw
- terge urmtorul cuvnt
D
- terge restul liniei curente
u
- anuleaz ultima comand
U
- anuleaz toate modificrile fcute pe ultima linie editat
r
- nlocuiete caracterul curent cu cel introdus
Cnd tergem ceva cu x, d (delete), sau alt comand, textul este salvat ntr-o
memorie temporar. l putem realipi cu p (paste, dei termenul tehnic n vi este
put). Comanda p are semnificaii diferite, n funcie de elementele de text terse.
Folosit dup dw (delete word):
p
P

- plaseaz textul dup cursor


- plaseaz textul nainte de cursor

Folosit dup dd:


p
P

- plaseaz textul sub linia curent


- plaseaz textul deasupra liniei curente

Modificarea unui cuvnt sau a unei pri de cuvnt (de la cursor pn la sfritul
cuvntului) se face prin poziionarea cursorului n locul de nceput i tastarea
comenzii cw (change word). Editorul trece automat n mod editare. Comanda cw
este o comand compus din alte dou comenzi; n acest caz, din alturarea
comenzilor c (change) i w (word). Alte exemple:
c$
c^
4cw
5dd
3x
3X
2G

- schimb ncepnd cu poziia curent pn la sfritul liniei


- schimb ncepnd cu poziia curent pn la nceputul liniei
- schimb urmtoarele 4 cuvinte
- terge urmtoarele 5 linii
- terge urmtoarele 3 caractere
- terge 3 caractere din faa cursorului
- poziioneaz cursorul la a doua linie a documentului

Copierea fr tergere se face cu y (yank). Nu afecteaz textul, dar poate fi folosit


n conjuncie cu p. n acest caz, p se comport la fel ca n cazul lui d.
yw
yy
3yy

- copiaz urmtorul cuvnt


- copiaz linia curent
- copiaz urmtoarele trei linii

Comanda de nlocuire r (replace) plaseaz vim n mod editare. Caracterul tastat va


nlocui caracterul curent, dup care editorul revine automat n mod comand. Pe
lng comanda r, vim trece n mod editare i cu i (insert), a (append) sau o
(open). n aceste cazuri, textul va fi introdus ncepnd cu prima poziie nainte de
cursor, prima poziie dup cursor, sau la nceputul unei linii noi, creat imediat sub
linia curent.
Pentru a cuta nainte ntr-un text, se folosete comanda /. De exemplu,
/text caut irul text de la poziia curent a cursorului ctre sfritul fiierului.
Cutarea napoi se face cu ? n loc de / . De exemplu, ?text caut de la poziia
curent a cursorului ctre nceputul fiierului.
Ultima comand discutat este cea de substituie, s (substitute). Permite
nlocuirea unor pri de text.
:[linii] s/text_vechi/text_nou/opiune
Vechiul text este nlocuit cu cel nou n limita liniilor specificate de domeniul
opional [linii]. Domeniul este specificat n format de la, pn la. Dac nu este
dat un domeniu, schimbrile apar numai pe linia curent, considerat implicit.
Cmpul opiune modific comanda. De obicei se folosete caracterul g, care
nseamn substituie global. Comanda
:s/test/text
nlocuiete cu text prima apariie a cuvntului test n linia curent. Dac doreti s
nlocuieti toate apariiile din linia curent, foloseti opiunea g
:s/test/text/g
Comanda
:1,10s/test/text
nlocuiete prima apariie a lui test n fiecare din cele 10 linii specificate. Ca s

schimbi toate apariiile lui text n aceste linii, adaugi opiunea g la sfrit.
Am acoperit numai comenzile de baz. Vim permite cteva comenzi foarte
sofisticate.13
Un lucru foarte important, toate uneltele folosite de noi n procesul de
asamblare (editorul de text, asamblorul) recunosc fiierele text ca fiiere scrise n
limbaj de asamblare dup extensia .asm. Cu alte cuvinte, fiierele surs scrise n
limbaj de asamblare trebuie ntotdeauna salvate pe disc cu extensia .asm (de ex.,
program.asm).
n continuare, scriei programul care urmeaz ntr-un fiier intitulat
prog.asm.
section .data
a
db
0fh
b
db
89
c
dw
045E3h
d
dw
65535
e
dd
001234567h
f
dd
1047000
g
db
0ffh
section .text
global _start
_start:
nop
;ncarc imediatul 8H n registrul de 8 bii AL
mov al, 8h
;ncarc imediatul 1239054 n registrul de 32 de bii EAX
mov eax, 1239054
;copiaz valoarea 89 (aflat n locaia de memorie cu adresa b) n registrul de 8 bii
BL
mov bl,[b]
;copiaz valoarea 45E3H (aflat n locaia de memorie cu adresa c) n registrul de
16 bii CX
mov cx,[c]
;copiaz valoarea 01234567H n registrul de 32 de bii EDX
mov edx,[e]
;ncarc registrul de 32 de bii EAX cu adresa etichetat a
mov eax,a
;ncarc registrul de 32 de bii EBX cu adresa etichetat b
mov ebx,b
13

ftp://ftp.vim.org/pub/vim/doc/book/vimbook-OPL.pdf

;copiaz n locaia de memorie etichetat a valoarea aflat n registrul de 8 bii AH.


n segmentul de date, a este declarat ca fiind etichet la o locaie de 8 bii.
mov [a],ah
;copiaz n locaia f de 32 de bii valoarea aflat n registrul de 32 de bii ECX.
Evident, vechea valoare adresat de f se pierde.
mov [f],ecx
;copiaz n registrul de 8 bii AH valoarea 0ffh. Observm parantezele ptrate. Fr
ele, asamblorul ar fi ncercat s scrie n AH adresa g, i cum registrul de 8 bii AH
este prea mic pentru o adres de 32 de bii, asamblorul returneaz o eroare
(relocation truncated to fit: R_386_16 against `.data').
mov ah, [g]
;ntrerupe rularea programului
mov eax,1
mov ebx,0
int 80h
Programul realizeaz una din cele mai frecvente activiti ntlnite n
practic, transfer date dintr-un loc n altul cu ajutorul instruciunii MOV.

4.5.2. Asamblarea
Dup editarea i salvarea programului putem verifica prezena fiierului
surs cu ajutorul comenzii ls. n urma acestei comenzi ar trebui s vedei fiierul
prog.asm. Dac nu, verificai s nu fi omis ceva. Presupunnd c fiierul surs este
prezent, trebuie s l transformm n fiier obiect cu ajutorul unui asamblor. n
paragrafele urmtoarea prezentm dou din cele mai cunoscute asambloare.
NASM (Netwide Assembler) este un asamblor pentru arhitecturi Intel x86,
de la 16 pn la 64 de bii, care suport toate extensiile acestora pn n prezent.
Ruleaz pe o mare varietate de sisteme de operare (Linux, BSD, Windows).
Folosete o sintax similar cu cea Intel, dar mai puin complex, i vine cu un
pachet serios de macroinstruciuni.
YASM este o rescriere complet a asamblorului NASM, sub o licen
diferit14. Suport mai multe sintaxe de asamblare (de ex. NASM, GAS, TASM,
etc.) i la fel de multe formate de fiiere obiect. n general, YASM poate fi folosit
alternativ cu NASM.
Avantaje:
a fost primul care a implementat suport pentru arhitecturi x86_64. Acesta a
fost i marele su avantaj pn la NASM 0.99.00.
14

Licen BSD, spre deosebire de NASM liceniat LGPL.

fiind un proiect mai dinamic, YASM rspunde mai rapid cererilor venite de
la utilizatori (de informaii sau noi opiuni).
poate asambla fiiere scrise att n sintaxa Intel ct i AT&T (gas).
implementeaz o interfa ce poate fi folosit de ctre compilatoare.
Dezavantaje:
NASM a fost utilizat i depanat intensiv. YASM este un proiect activ, e
posibil s nu fie att de bine testat ca NASM.
Datorit resurselor superioare implicate de-a lungul timpului n dezvoltarea
NASM, documentaia este mai complet.

NASM i YASM sunt utilitare n linie de comand. Deoarece n testele


noastre YASM s-a dovedit a suporta mai bine arhitecturi de 64 de bii, n aceast
carte recomandm utilizarea acestuia. n schimb, pentru informaii cu privire la
sintaxa, instruciunile, declaraiile suportate de asamblor, consultai manualul
NASM.15
Asamblai fiierul surs introducnd urmtoarea comand:
yasm -f elf -g stabs prog.asm -l prog.lst
Comanda de asamblare ncepe chiar cu numele asamblorului. Dup nume
urmeaz diverse opiuni ce guverneaz procesul de asamblare. Acestea pot fi gsite
n documentaia YASM. Cele prezente sunt specifice platformei Linux:
cuvntul YASM invoc asamblorul;
-f elf specific asamblorului faptul c fiierul obiect trebuie generat n
format ELF, propriu sistemelor Linux de 32 de bii;
-g specific asamblorului s includ n fiierul obiect informaie de
depanare i indic formatul acesteia (n acest caz STABS);
prog.asm, numele fiierului surs pe care dorim s l asamblm;
-l specific asamblorului s creeze un listing al procesului de asamblare.
Fiierul listing include textul programului i eventualele erori aprute la
asamblare.
Dac YASM nu gsete greeli de sintax, procesul se finalizeaz cu
succes i obinem doar un nou prompt. Dac ai scris programul cu greeli,
asamblorul semnaleaz atenionri sau erori. Despre acestea vorbim mai trziu,
deocamdat asigurai-v c fiierul prog.lst corespunde celui de mai jos. Tastai
15

http://www.nasm.us/doc/. Totui, nu neglijai resursele YASM: http://www.tortall.net/


projects/yasm/manual/manual.pdf

comanda cat prog.lst.


1
2
3 00000000
4 00000001
5 00000002
6 00000004
7 00000006
8 0000000A
9 0000000E
10
11
12
13 00000000
15 00000001
17 00000003
19 00000008
21 0000000E
23 00000015
25 0000001B
27 00000020
29 00000025
31 0000002B
33 00000031
35 00000037
36 0000003C
37 00000041

%line 1+1 prog.asm


[section .data]
0F
a db 0f
59
b db 89
E345
c dw 045E3
FFFF
d dw 65535
67452301
e dd 001234567
D8F90F00
f dd 1047000
FF
g db 0ff
[section .text]
[global _start]
_start:
90
nop
B008
mov al, 8
B80EE81200
mov eax, 1239054
8A1D[00000000]
mov bl,[b]
668B0D[00000000]
mov cx,[c]
8B15[00000000]
mov edx,[e]
B8[00000000]
mov eax,a
BB[00000000]
mov ebx,b
8825[00000000]
mov [a],ah
890D[00000000]
mov [f],ecx
8A25[00000000]
mov ah, [g]
B801000000
mov eax,1
BB00000000
mov ebx,0
CD80
int 80

Acesta este rezultatul opiunii -l din comanda de asamblare. Chiar dac


procesul de asamblare nu a ntmpinat probleme, informaia din fiierul prog.lst nu
este lipsit de importan. Cele mai interesante informaii se desprind din coloana a
doua i a treia. Coloana a doua specific distana liniei curente fa de nceputul
fiierului surs (dat n octei). Din aceste informaii putem calcula numrul de
octei ai instruciunilor. A treia coloan prezint codul main pentru fiecare
instruciune n limbaj de asamblare. Codul main al instruciunilor care lucreaz
cu memoria sunt incomplete (vezi parantezele ptrate), deoarece n momentul
asamblrii nc nu sunt cunoscute adresele la care se vor afla datele n memorie.
Acesta este rolul editorului de legturi.
Formatul fiierelor obiect variaz n funcie de platforma pe care dorim s
rulm programul. YASM este capabil s genereze toate formatele ntlnite pe

platformele suportate. Parametrul -f specific asamblorului formatul fiierului


obiect. Pentru arhitecturi IA-32 i sistem de operare Linux i386, formatul este
ELF32, sau simplu, ELF. Pentru arhitecturi x86-64 i sistem Linux amd64,
formatul este ELF64.
n timpul dezvoltrii unui program este dezirabil s includei n fiierul
obiect informaie de depanare. n acest mod se poate verifica execuia programului
i depista eventualele erori de programare. Parametrul -g specific asamblorului s
introduc informaie de depanare n fiierul obiect. La fel ca n cazul formatului
obiect, YASM poate genera diferite formate de informaie de depanare. Pentru un
sistem Linux i o platform de 32 de bii se poate folosi formatul STABS sau
DWARF. Formatul STABS este mai simplu i e tot ce ne trebuie n acest stadiu.
Reinei c sistemul de operare Linux difereniaz literele mici de cele mari
n corpul comenzilor (case sensitive). Parametrul -f este diferit de parametrul -F.
La fel i numele fiierului surs: prog.asm este diferit de Prog.asm, sau orice alt
variaie de caractere. Acelai lucru este valabil i n cazul etichetelor din program.
Asamblorul difereniaz eticheta b de B. n schimb, numele registrelor pot aprea
n orice variant: eax, EAX, eAx, Al, aL, toate sunt corecte att timp ct
succesiunea de litere denot un registru existent. S intrm puin n detalii.
Mesaje de eroare
Un fiier surs scris corect este complet comprehensibil asamblorului i
poate fi translatat n instruciuni main fr probleme. Dac asamblorul gsete
ceva ininteligibil n linia de program pe care o proceseaz, textul respectiv se
numete eroare, i asamblorul afieaz un mesaj de eroare. Erorile pot consta din
scrierea greit a numelui unui registru sau din asocierea eronat a opcode-ului cu
operanzii. n cazul primei erori, mesajul de eroare afiat este de forma:
prog.asm:5: symbol 'etx' undefined
n acest caz mesajul este destul de clar, asamblorul nu cunoate cine este
etx. Pentru noi este un registru scris greit. Pentru asamblor poate fi orice simbol al
programului, nu conteaz ce anume (putea fi numele unei variabile), problema este
c nu l nelege. Observai c numrul liniei care conine eroarea este plasat
imediat dup numele fiierului (:5).
Alturarea eronat a opcode-ului cu operanzii este semnalizat prin
mesajul:
prog.asm:10: invalid combination of opcode and operands
Eti avertizat c ai ncercat o asociere invalid ntre opcode i operanzi.
Dar, ca s poi nelege ce este valid i ce este invalid trebuie s tii ce ai greit.
Aadar, mesajele de eroare ale asamblorului nu absolv programatorul de la

cunoaterea procesorului sau regulilor de asamblare. Atunci cnd asamblorul


afieaz concomitent mai multe erori, este indicat s ncepei cu rezolvarea
primeia. Celelalte erori pot aprea din cauza acesteia.
Mesaje de eroare pot aprea i n etapa de editare de legturi, atunci cnd
editorul de legturi ntmpin probleme n procesul de asociere a mai multor fiiere
obiect. Din fericire, aceste erori sunt mai puin ntlnite.
Mesaje de atenionare
Mesajele de eroare fac imposibil generarea fiierului executabil; cnd
asamblorul sau editorul de legturi ntlnete o eroare, nu va furniza ca ieire
niciun fiier.16 Mesajele de atenionare apar cnd asamblorul ntlnete ceva care
violeaz logica sa intern, dar nu att de grav nct s opreasc procesul de
asamblare. De exemplu, YASM va afia o atenionare dac n program este definit
o etichet creia nu i urmeaz nicio instruciune. n cazul atenionrilor,
asamblorul se comport ca un consultant care indic ceva ciudat n codul surs.
Dei executabilul va fi generat, este bine s inem cont de mesajele de atenionare
i s investigm problemele aprute. Ignorai un mesaj de atenionare numai dac
tii exact ce nseamn.

4.5.3. Editarea legturilor


n acest moment, YASM a generat un fiier obiect cu numele fiierului
surs i extensia .o (object). Nu este neaprat necesar ca fiierul obiect s poarte
numele fiierului surs. Numele fiierului obiect rezultat poate fi specificat prin
parametrul -o. nainte s editm legturile, verificm existena fiierului obiect cu
ls.
Editorul de legturi are rolul s uneasc fiierul obiect de altele i s
aloce efectiv adrese i resurse de sistem viitorului fiier executabil. Editorul de
legturi utilizat este ld (GNU Linker). Dup ce ne-am asigurat de prezena
fiierului obiect prog.o scriem n terminal urmtoarea comand:
ld -o prog prog.o
Dac procesul se finalizeaz fr erori, n directorul curent apare un fiier
executabil. La fel ca la asamblare, parametrul -o specific numele fiierului de
ieire. Dac nu am fi specificat numele dorit pentru executabil, editorul de legturi
ar fi generat un fiier cu numele a.out. n Windows, fiierele executabile se
identific prin extensia .exe. n Linux, executabilele nu folosesc extensie, dar pot fi
16

Aceste erori sunt fatale. De aici i termenul fatal error.

identificate dup drepturile de execuie pe care le dein sau dup culoare verde din
listingul ls.
Atenie! Dac editorul de legturi refuz s proceseze comanda anterioar
i afieaz mesajul ld: i386 architecture of input file `prog.o' is incompatible with
i386:x86-64 output, nseamn c sistemul de operare este de tip x86-64 i editorul
de legturi nu poate lega un fiier obiect generat n format ELF de biblioteci de 64
de bii. O soluie este s asamblai iari programul folosind formatul ELF64. Dar,
pentru c subiectul acestei cri este studiul limbajului de programare pe arhitecturi
IA-32, este mai indicat s generai un executabil compatibil cu astfel de arhitecturi
folosind n procesul editrii de legturi parametrul -m, astfel:
ld -o prog prog.o -m elf_i386

4.5.4. Automatizarea sarcinilor cu GNU Make


Probabil deja v gndii cu groaz la faptul c de acum nainte, pentru
fiecare program n parte, trebuie s scriei o linie complex de asamblare, plus una,
la fel de complex, de editare de legturi. Dar v atenionez c acest lucru se
ntmpl nu dup fiecare instan de program, ci dup fiecare modificare a
programului. Se poate ntmpla s scriei o virgul unde nu trebuie. Procesul de
generare a executabilului trebuie luat de la nceput: editare, asamblare, editare de
legturi. Atunci cnd aplicaia dispune de mai multe fiiere surs, situaia devine
critic. Asamblarea i legarea acestora devine un proces foarte anevoios.
Utilitarul Make servete n Linux la automatizarea compilrii programelor.
Cele mai multe programe distribuite n format surs folosesc acest utilitar. Prima
implementare a GNU Make a fost realizat de Richard Stallman i Roland
McGrath
Mecanismul Make permite crearea fiierelor executabile din prile lor
componente. Utilitarul Make execut alte programe n conformitate cu un plan
descris de un fiier text numit makefile. Fiierul makefile seamn puin cu un
program de calculator n care se specific cum trebuie s se fac ceva. Dar, spre
deosebire de un program, nu precizeaz succesiunea exact a operaiilor ci
specific ce componente din program sunt necesare la crearea altor componente din
program. n cele din urm, prin aceast modalitate de aciune, sunt definite toate
regulile necesare obinerii executabilului final. Aceste reguli sunt numite
dependine.
n practic, codul surs al aplicaiilor conine sute, mii sau chiar milioane
de linii. Problema principal a dezvoltatorilor de aplicaii const din cantitatea
imens de linii de cod care trebuie gestionat. Rspunsul la aceast problem
const n scrierea programelor ntr-o manier modular. Programul este mprit n
componente mai mici i fiecare component este dezvoltat separat. Desigur, acest

lucru nseamn o provocare suplimentar, trebuie s tii cum sunt create


componentele i modul n care acestea se potrivesc mpreun. Pentru asta este
nevoie de un plan.
Noi deja am urmat un astfel de plan. Ca s obinem fiierul executabil a
trebuit s transformm mai nti fiierul surs n fiier obiect (cu ajutorul unui
asamblor). Existena fiierului obiect depinde aadar de cea a fiierului surs. n
continuare, fiierul executabil depinde de existena fiierului obiect, precum i de
existena altor fiiere bibliotec. Pentru legarea acestor fiiere obiect mpreun am
fost obligai s folosim un editor de legturi.
Ei bine, fiierul makefile trebuie s descrie chiar acest plan. Crearea
fiierului makefile ncepe cu determinarea dependinelor (fiierele necesare pentru
obinerea fiierului executabil). Fiierul executabil este creat n etapa de editare de
legturi, aadar, prima dependin care trebuie definit const din numele fiierelor
necesare editorului de legturi ca s creeze fiierul executabil. Prima linie a
fiierului makefile este:
prog: prog.o
Acest lucru nseamn c pentru generarea fiierului executabil prog este nevoie de
fiierul obiect prog.o. Aceast linie se numete linie de dependine. Probabil este
cea mai simpl linie de dependine posibil: un fiier executabil depinde de un
singur fiier obiect. Dac sunt necesare fiiere obiect adiionale, ele trebuie aranjate
unul dup altul, separate prin spaii. Aceast organizare se numete list de
dependine.
exec: main.o swap.o com.o
Aceast linie ne spune c executabilul depinde de trei fiiere obiect (main.o,
swap.o i com.o). Toate trei trebuie s existe nainte de a putea genera fiierul
executabil exec, numit int. Liniile de dependine descriu fiierele, nu i aciunea
necesar obinerii lor. Aceasta este o parte esenial a procesului, i este o linie
cunoscut nou. Aceste dou linii lucreaz mpreun:
prog: prog.o
ld -o prog prog.o
A doua linie trebuie identat fa de nceputul liniei cu un singur caracter TAB.
Din motive de tradiie, comenzile dintr-un fiier makefile trebuie precedate de
caracterul TAB. O greeal frecvent const n introducerea unor spaii nainte de
comenzi. Apariia unui mesaj de forma celui de mai jos nseamn, destul de
probabil, omiterea folosirii caracterului TAB:

makefile:2: *** missing separator.

Stop.

Aadar, mecanismul Make presupune s specifici numele fiierelor necesare i ce


trebuie fcut cu aceste fiiere. n plus, pentru a optimiza execuia aciunilor, o dat
fiierul compilat sau legat, Make nu va repeta procesul dect atunci cnd este
modificat unul din fiierele surs sau obiect de care depinde. De asemenea, Make
tie cnd au fost modificate ultima oar executabilul i dependinele, astfel nct,
dac fiierul executabil este mai recent dect vreo dependin, deduce c
schimbrile aprute n codul obiect deja sunt reflectate n executabil (poate fi
absolut sigur de acest lucru, deoarece singura modalitate de a genera executabilul
se face prin prolucrarea fiierului obiect). Make determin data i ora modificrilor
printr-o facilitate a sistemului de fiiere (amintii-v de rezultatul comenzii ls l).
nc nu am terminat de editat fiierul makefile. Mecanismul Make i
dovedeasc importana cnd fiierul de configurare conine lanuri de dependine.
Chiar i cel mai simplu fiier makefile va conine dependine care depind de alte
dependine. Pn i programul nostru banal are nevoie de dou propoziii de
dependine n fiierul su makefile:
prog: prog.o
ld -o prog prog.o
prog.o: prog.asm
yasm -f elf -g stabs prog.asm
Aceste dou propoziii de dependine definesc cei doi pai necesari
generrii fiierului executabil prog.
La apelarea comenzii make, aceasta caut n directorul curent fiierul
makefile (dac nu l gsete, caut Makefile) i execut secvena de operaii
specificat n el.
make -k
Opiunea -k instruiete Make s opreasc generarea oricrui fiier n care apare o
eroare i s pstreze neschimbat copia anterioar a fiierului int (continu ns
generarea oricrui alt fiier care trebuie creat/recreat). n absena opiunii -k, Make
ar putea suprascrie codul obiect i executabilul existent cu variante incomplete.
Reamintim c oricnd aducei fiierului surs o modificare, indiferent de
mic, trebuie s rulai make pentru a reface executabilul. Lucru care nu este att
de groaznic cum pare. Editoarele de text prezentate includ combinaii de taste sau
comenzi care v permit s rulai Make din cadrul lor.

Integrarea cu Vim
Comanda :make ruleaz programul Make n directorul curent. Implicit,
Vim recunoate mesajele de eroare i le listeaz, facilitnd deplasarea prin surse.
Deplasarea ntre mesajele de eroare se face utiliznd comenzile :cnext i
:cprev. Aceste comenzi funcioneaz i dac eroarea nu se afl n fiierul curent
(deschide automat fiierele care conin eroarea).
Comanda :copen mparte ecranul pe orizontal i deschide o fereastr
nou, dedicat afirii erorilor. Poziionarea cursorului pe o anumit eroare, urmat
de un Enter sare n programul surs la linia respectiv. Navigarea ntre ferestre se
face cu comenzile de deplasare cunoscute (h,j,k,l) precedate de Ctrl-w.
nchiderea ferestrelor se face normal,:q. Fereastra cu erori poate fi creat la
momentul execuiei Make dac se folosete comanda compus :make | copen.
Dac se prefer mprirea ecranului pe vertical se folosete :make | vert
copen.
Aa cum am spus mai devreme, dac fiierul executabil este mai nou dect
toate fiierele de care depinde, Make va refuza s l regenereze. Cu toate acestea,
exist situaii cnd acest lucru este dezirabil. n special cnd modificai fiierul
makefile i dorii s l testai. Linux pune la dispoziie o comand, numit touch,
care are un singur rol: actualizeaz informaiile de timp ale fiierelor. Dac
executai comanda touch asupra fiierului surs sau obiect, de exemplu touch
prog.asm, fiierul va deveni mai nou dect fiierul executabil i Make va
repeta procesul de generare. Editorul Vim permite rularea comenzilor din linia sa
de comand. De exemplu, comanda anterioar poate fi executat din interiorul
editorului tastnd :!touch prog.asm.

4.5.5. Execuia programului


Odat fiierul binar obinut, l putem executa prin apelarea sa din linie de
comand:
./firstProg
Probabil v ateptai s nu se ntmple nimic. Instruciunile programului
nu cer date de la tastatur i nici nu afieaz ceva pe ecran. Programul nu face
altceva dect s introduc date n cteva locaii de memorie i s transfere valori
ntre registre i memorie. Singurul mod n care putem verifica execuia
programului este s aruncm o privire n interiorul calculatorului. Nu deschidei
carcasa, din fericire dispunem chiar de unealta de care avem nevoie: depanatorul
GDB. Dar, pn atunci, s vorbim despre problemele care pot fi ntmpinate chiar
dac procesul de asamblare s-a desfurat fr eroare.

Greeli de programare
Dac n urma procesului de asamblare se obine fiierul executabil,
nseamn c din punct de vedere al sintaxei programul este corect. Aceasta nu
nseamn ns c programul va face ceea ce inteniona programatorul s fac. Se
spune c programul care nu lucreaz conform planului conine una sau mai multe
greeli de programare (bug). O greeal de programare este acel ceva din program
care nu lucreaz aa cum a gndit programatorul s lucreze.
O eroare denot prezena n fiierul surs a ceva inacceptabil pentru
asamblor sau editor de legturi. O eroare previne finalizarea cu succes a procesului
de generare a fiierului executabil. n contrast, o greeal de programare este o
problem descoperit n timpul execuiei unui program. Greelile de programare,
sau de algoritm, nu sunt detectate de asamblor sau de editorul de legturi. Pot fi
beningne, de exemplu cuvinte scrise greit ntr-un mesaj destinat afirii pe ecran,
sau pot cauza ntreruperea prematur a programului. n unele cazuri, greelile de
programare pot ntrerupe execuia programului fr mesaje de avertizare. n
situaiile n care operaiile programului afecteaz sistemul de operare (ncearc s
acceseze zone de memorie protejate, etc.), acesta va ntrerupe programul i va afia
o eroare:
Segmentation Fault
Acest tip de eroare se numete eroare la rulare (runtime error).
De cele mai multe ori ns, programul nu va deranja sistemul de operare,
va rula complet, dar rezultatul nu va fi cel ateptat.

4.5.6. Depanarea fiierului executabil


Testarea programelor i eliminarea erorilor de programare se face n cadrul
unui proces numit depanare (debugging). Depanarea se refer la procesul
sistematic prin care sunt localizate i corectate greelile de programare.
Depanatorul este un program creat special n scopul localizrii i identificrii
greelilor de programare. Permite ncrcarea programului n memorie, execuia sa
pas cu pas17 i vizualizarea memoriei i a registrelor procesorului. n Linux,
depanatorul standard este GDB (GNU Debugger). Autorul este Richard Stallman,
care a nceput dezvoltarea acestuia n anul 1988. Ruleaz n linie de comand i a
fost creat pentru a depana programe scrise n C/C++.
Pentru a putea fi executat prin intermediul unui depanator, fiierul
executabil trebuie s fie asamblat cu opiunea -g (include informaia de depanare).
17

Execuia programului se oprete dup efectuarea fiecrei instruciuni main.

Nu orice executabil include aceast informaie. n general, trebuie s folosim


informaii de depanare numai dac vrem s verificm aplicaia respectiv. Chiar
dac executabilul creat cu opiunea -g ruleaz i se comport ca orice alt program
asamblat fr, deoarece introduce n fiierul executabil informaie adiional,
executabilul devine mai mare i mai lent.
Presupunnd c executabilul conine informaia de depanare necesar,
putem rula programul prin intermediul GDB astfel:
gdb executabil
GDB are o interfa de tip interpretor de comenzi ce permite execuia comenzilor
de depanare. Prompt-ul (gdb) arat c se ateapt din partea programatorului
introducerea unei comenzi de depanare. Ieirea din GDB se face prin comanda
quit. Putem lansa n execuie GDB fr s furnizm ca argument numele
fiierului executabil. n acest caz, specificarea acestuia se face prin comanda file.
stefan@laptop:~$ gdb
GNU gdb (Ubuntu/Linaro 7.2-1ubuntu11) 7.2
Copyright (C) 2010 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-linux-gnu".
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
(gdb)
GDB poate fi lansat n execuie cu opiunea -q (quiet). n acest caz textul de
prezentare nu mai este afiat.
stefan@laptop:~$ gdb -q
(gdb) file prog
Reading symbols from /home/stefan/prog...done.
Comanda help arat clasele de comenzi pe care le poate primi depanatorul.
Afiarea comenzilor coninute ntr-o clas se face prin specificarea numelui
acesteia imediat dup comanda help. Descrierea unei comenzi se face prin acelai
procedeu, numai c de aceast dat, dup help specificm numele comenzii.
(gdb) help info variables

All global and static variable names, or those matching REGEXP.


(gdb) help info registers
List of integer registers and their contents, for selected stack frame.
Register name as argument means describe only that register.
Comanda list afieaz codul surs al programului, linia sau funcia specificat.
Fr argument, afieaz zece (numrul implicit) sau mai multe linii aflate dup sau
n jurul liniei la care s-a ajuns n urma afirii anterioare.
(gdb) list
1
section .data
2
r
db
3
a
db
4
b
db
5
c
dw
6
d
dw
7
e
dd
8
f
dd
9
g
db
10
section .text

67
0fh
89
045E3h
65535
001234567h
1047000
0ffh

n cazul n care argumentul specific o linie, sunt afiate zece linii n jurul acelei
linii.
(gdb) info source
Current source file is prog.asm
Located in /home/stefan/prog.asm
Contains 29 lines.
Source language is unknown.
Compiled with stabs debugging format.
Does not include preprocessor macro info.
(gdb) list 19
14
mov al, 8h
15
mov bl,[r]
16
mov [r],al
17
mov eax, 1239054
18
mov bl,[b]
19
mov cx,[c]
20
mov edx,[e]
21
mov eax,a
22
mov ebx,b

23

mov [a],ah

mpreun cu argumentul (liniu) afieaz zece linii dinaintea poziiei la care s-a
ajuns prin listarea anterioar.
(gdb) list 4
5
6
7
8
9
10
11
12
_start:
13

b
db
c
dw
d
dw
e
dd
f
dd
g
db
section .text
global _start

89
045E3h
65535
001234567h
1047000
0ffh

nop

Numrul implicit de linii de afiare poate fi schimbat cu ajutorul comenzii set.


(gdb) set listsize 5
(gdb) show listsize
Number of source lines gdb will list by default is 5.
(gdb) list
14
mov al, 8h
15
mov bl,[r]
16
mov [r],al
17
mov eax, 1239054
18
mov bl,[b]
Avem posibilitatea s precizm exact regiunea dorit. Urmtoarea comand
afieaz tot programul. Numrul total de linii al programului a fost deja aflat cu
comanda info source. Comanda list a fost abreviat cu l.
(gdb) l 1,29
1
section .data
2
r
3
a
4
b
...
7
e
8
f
9
g

db
db
db

67
0fh
89

dd
dd
db

001234567h
1047000
0ffh

10
11
12
13
...
27
28
29

section .text
global _start
_start:
nop
mov eax,1
mov ebx,0
int 80h

Pentru a executa programul pas cu pas trebuie s specificm un punct de oprire.


Punctul de oprire (breakpoint) stopeaz execuia programului la acel punct din
codul surs. De acolo putem rula programul pas cu pas sau putem continua
execuia obinuit. Punctul de ntrerupere ne permite s inspectm starea
programului la momentul respectiv.
Punctele de ntrerupere pot fi specificate folosind numrul de linie al
codului surs, numele unei funcii sau o adres. Comanda este break (se poate
abrevia cu b). Putem obine informaii cu privire la punctele de ntrerupere cu
info breakpoints.
(gdb) b 15
Breakpoint 1 at 0x8048083: file prog.asm, line 15.
(gdb) b *_start+1
Breakpoint 2 at 0x8048081: file prog.asm, line 14.
Observaie: b *_start+1 este comanda utilizat de noi pentru toate
exemplele din aceast carte i trebuie s indice ctre o instruciune NOP, altfel
punctul de ntrerupere setat nu va fi luat n considerare (un bug al depanatorului
GDB).
(gdb) info b
Num Type
1
breakpoint
2
breakpoint

Disp Enb Address What


keep y 0x08048083 in _start at prog.asm:15
keep y 0x08048081 in _start at prog.asm:14

Observai c fiecrui punct de ntrerupere i este atribuit un numr (indice) care ne


permite s l adresm. Coloana Disp (Disposition) descrie decizia care trebuie
ndeplinit de depanator cnd ajunge n punctul respectiv. Implicit, toate punctele
de ntrerupere sunt executate (keep). Punctul de ntrerupere mai poate fi marcat cu
dezactiveaz (disable) sau terge (delete). Coloana Enb (Enabled) specific dac
punctul de ntrerupere este iniializat sau nu. Un y indic faptul c este iniializat.
Adresa de memorie la care se afl punctul de ntrerupere este dat n coloana

Address, iar adresa din fiierul surs, afiat ca o combinaie de nume de fiier i
numr de linie, este dat de ultima coaloan.
Comanda tbreak (temporary break) seteaz un punct de ntrerupere
temporar. Un punct de ntrerupere temporar ntrerupe execuia programului o
singur dat, apoi este ters.
(gdb) tbreak 13
Temporary breakpoint 4 at 0x8048080: file prog.asm, line 13.
(gdb) info b
Num Type
Disp Enb Address What
1
breakpoint keep y 0x08048083 in _start at prog.asm:15
2
breakpoint keep y 0x08048081 in _start at prog.asm:14
4
breakpoint del y 0x08048080 in _start at prog.asm:13
Comanda disable dezactiveaz punctul de ntrerupere. Primete ca
argument numrul punctului de ntrerupere. Fr argumente dezactiveaz toate
punctele de ntrerupere. Activarea unui punct de ntrerupere se face cu enable.
(gdb) disable 2
(gdb) info b
Num Type
Disp Enb Address What
1
breakpoint keep y 0x08048083 in _start at prog.asm:15
2
breakpoint keep n 0x08048081 in _start at prog.asm:14
4
breakpoint del y 0x08048080 in _start at prog.asm:13
Ignorarea punctului de ntrerupere pentru un anumit numr de ori se
realizeaz cu comanda ignore. Comanda ignore primete dou argumente:
numrul punctului de ntrerupere ce trebuie ignorat i numrul de ori pentru care
acesta este srit.
tergerea unui punct de ntrerupere se face cu comanda delete.
Argumentul specific numrul punctului de ntrerupere ters. Dac nu sunt
specificate argumente, terge toate punctele de ntrerupere.
(gdb) delete
Delete all breakpoints? (y or n) y
(gdb) info b
No breakpoints or watchpoints.
Lansarea n execuie a programului se face prin comanda run. Programul
se oprete la punctul de ntrerupere. De acolo putem continua execuia pas cu pas
prin comanda next (abreviat n) sau step (abreviat s). Dei n cazul

programului actual next i step se comport la fel, ele sunt comenzi diferite.
Pentru o linie de cod care apeleaz o funcie, next va sri peste funcie, la
urmtoarea linie de cod, n timp ce step va intra n funcie.
Execuia programului depanat poate fi oprit oricnd cu comanda kill.
Starea programului poate fi consultat cu info program.
(gdb) b *_start+1
Breakpoint 1 at 0x8048081: file prog.asm, line 14.
(gdb) r
Starting program: /home/stefan/prog
Breakpoint 1, _start () at prog.asm:14
warning: Source file is more recent than executable.
14
mov al, 8h
(gdb) n
15
mov bl,[r]
(gdb) s
16
mov [r],al
(gdb) info program
Using the running image of child process 4717.
Program stopped at 0x8048089.
It stopped after being stepped.
Continuare execuiei se face cu ajutorul comenzii continue. Ieirea din
GDB se face prin comanda quit.
Coninutul registrelor este afiat cu info registers, info
registers $registru, info all-registers. Versiunile recente primesc ca
parametru i numele simplu al registrului, fr simbolul $. O abreviere util este de
genul i r ax. Tot de la versiunile mai noi se poate afia coninutul registrelor
mai mici dect cuvntul standard al arhitecturii (32 de bii). Eu am observat aceast
posibilitate de la versiunea 7.2.
(gdb) info registers
eax
0x8 8
ecx
0x0 0
edx
0x0 0
ebx
0x4367
esp
0xffffd400 0xffffd400
ebp
0x0 0x0
esi
0x0 0

edi
0x0 0
eip
0x8048089 0x8048089 <_start+9>
eflags
0x202
[ IF ]
cs
0x23 35
ss
0x2b 43
ds
0x2b 43
es
0x2b 43
fs
0x0 0
gs
0x0 0
(gdb) info registers $eax
eax
0x8 8
(gdb) info registers $eax $ebx $ecx
eax
0x8 8
ebx
0x4367
ecx
0x0 0
(gdb) i r al
Comanda care afieaz valorile locaiilor de memorie este x (examine). Este o
comand complex, care poate primi mai muli parametrii opionali. Apare n
urmtoarea form:
x /nyz &etichet
unde:

n reprezint numrul de linii de afiat,


y descrie formatul n care se dorete afiarea,
z descrie mrimea unitii n care se dorete afiarea.
Tabel 4.3: Detalii cu privire la parametrii opionali

Numr de repetiie (ntreg zecimal)


Specific numrul de uniti de memorie (n z) care trebuie afiat.
Valoarea implicit este 1.

Formatul afirii
x
hexazecimal
d
zecimal (decimal)
u
zecimal fr semn (unsigned decimal)
o
octal
t
binar (two)
afieaz adresa att n zecimal ct i ca deplasament fa de cel
a
mai apropiat simbol precedent.
c
afieaz sub form de caractere (character)

s
afieaz ca ir terminat n caracterul 0
t
afieaz ca numr n virgul mobil
i
afieaz ca instruciune n cod main
Valoarea implicit este x. Valoarea implicit se schimb de fiecare dat
cnd este folosit comanda x.
Mrimea unitii
b
octei (byte)
h
2 octei sau jumti de cuvnt (half-word)
w
4 octei sau cuvnt de 32 de bii (word)
g
8 octei sau cuvnt gigant (giant word)
Valoarea implicit este w. Valoarea implicit se modific automat la
fiecare unitate care este specificat cu comanda x.

(gdb) x /1db &r


0x80490d0 <r>:
(gdb) x /1xb &r
0x80490d0 <r>:
(gdb) x /1dt &r
0x80490d0 <r>:
(gdb) x /1c &r
0x80490d0 <r>:
(gdb) x /4db &r
0x80490d0 <r>:
(gdb) x /4xb &e
0x80490d7 <e>:

67
0x43
01000011
67 'C'
67

15

89

-29

0x67

0x45

0x23

0x01

Comanda care poate afia att valoarea unui registru ct i a unei locaii de
memorie este print. Comanda print poate folosi parametrii opionali de
formatare a afirii.
Registrele primite ca argument trebuie precedate de caracterul $ (dollar).
(gdb) print /x $eax
$4 = 0x12e80e
(gdb) print /d $ebx
$5 = 89
(gdb) print /t $ecx
$6 = 100010111100011
n cazul locaiilor de memorie, o etichet simpl dat ca argument duce la
afiarea valorii, o etichet precedat de caracterul & (ampersand) arat adresa
locaiei de memorie respective.

(gdb) print /x &e


$11 = 0x80490d7
(gdb) print /x e
$12 = 0x1234567
Dac suntei obinuii cu limbajul de programare C, comanda printf v
este deja cunoscut. Comanda poate executa o expresie de afiare complex,
utiliznd un format ir cu specificatori de conversie exact ca la funcia ANSI C,
printf().
(gdb) printf "%d\n",r
3
(gdb) printf "%d\n", $eax
8
(gdb) printf "%x\n", &r
80490d0
Comanda backtrace (abreviat bt) afieaz lista funciilor invocate din
programul principal pn n momentul respectiv. Lista conine cte o linie pentru
fiecare cadru de stiv (stack frame). Comenzile frame i info frame ne permit
s examinm coninutul cadrelor de stiv. Selectarea unui cadru se face prin
specificarea numrului su ca argument pentru comanda frame. Odat selectat,
putem observa ce conine cu ajutorul comenzii info frame.
Depanatorul poate dezasambla o seciune de memorie specificat. Poate
primi ca argument o adres, de exemplu _start. n mod implicit, zona de
memorie este afiat n sintaxa AT&T. Prima comand din urmtoarea secven
schimb modalitatea de reprezentare.
(gdb) set disassembly-flavor intel
(gdb) disassemble _start
Dump of assembler code for function _start:
0x08048080 <+0>: nop
0x08048081 <+1>: mov eax,0x80490c8
0x08048086 <+6>: mov ebx,DWORD PTR ds:0x80490c8
End of assembler dump.
Ca s nu fim nevoii s specificm sintaxa dorit la fiecare deschidere a
depanatorului, scriei ntr-un terminal urmtoarea comand:
echo "set disassembly-flavor intel" > ~/.gdbinit

4.6. ntrebri i exerciii


4.1. Care este structura unui program n limbaj de asamblare?
4.2. Care este diferena dintre program i proces?
4.3. Care este diferena ntre datele iniializate i neiniializate?
4.4. Conform crui criteriu aranjeaz un procesor Intel x86 octeii de date n
memorie?
4.5. Pentru fiecare din urmtoarele declaraii, reprezentai harta memoriei.
msg dw Introduceti primul numar:
d
dd
123
w
dw
AB
sir db
ABCD
4.6. Ce nseamn ELF i STABS n linia de asamblare?
4.7. Ce este un mesaj de eroare? Prin ce se difereniaz acesta de mesajele de
atenionare?
4.8. Ce rol are opiunea m elf_i386 din linia editorului de legturi?
4.9. Ce rol are un depanator? Cu ce opiune trebuie s asamblm un program astfel
nct s l putem depana?
4.10. Ce este un punct de ntrerupere?
4.11. Ce rol are utilitarul Make?
4.12. Cum se execut un program din linie de comand n Linux?

5. MEMORIA

Acest capitol demonstreaz modul de declarare a datelor n program i


importana alinierii acestora n memorie. Discut n detaliu diferite metode de
adresare disponibile la procesoarele Intel de 32 de bii i descrie modul de
implementare i manipulare n limbaj de asamblare a vectorilor. Din perspectiva
unui programator n asamblare, acest capitol, deoarece prezint metodele de
adresare a memoriei, este unul din cele mai importante, dac nu cel mai important.

5.1. Declararea datelor n bloc


Prefixul TIMES permite repetarea unei instruciuni sau pseudoinstruciuni
de un numr specificat de ori. n exemplul de mai jos, TIMES repet de 100 de ori
pseudoinstruciunea db 0, rezultnd un buffer de 100 de octei de 0. Pentru
obinerea aceluiai rezultat, dar fr prefixul TIMES, ar fi trebuit s scriem dup
directiva DB o sut de cifre de zero separate prin virgul. N-ar fi fost foarte plcut.
buffer:

times 100 db

Dar TIMES este mai versatil dect att. Argumentul lui TIMES (n
exemplul precedent argumentul a fost 100) nu este o constant numeric, ci o
expresie numeric. n consecin, se pot construi propoziii mult mai complexe.
buffer:
times

db
'Rezervat:'
19-$+buffer
db '-'

Expresia 19-$+buffer se evalueaz la 10, aadar se repet de 10 ori


db '-'(caracterul liniu). n final, n memorie vom avea secvena:
Rezervat:----------.
Sunt i situaii n care nu este indicat folosirea prefixului TIMES. De
exemplu, propoziiile times 100 resb 1 i resb 100 au acelai rezultat,
numai c ultima, din cauza structurii interne a asamblorului, este asamblat de 100
de ori mai rapid.
Totui, ca s nu fim acuzai de partizanat, programul care demonstreaz

faptul c datele neiniializate sunt alocate la momentul rulrii, i nu asamblrii,


folosete la rezervarea ambelor tipuri de date prefixul TIMES. n primul rnd, s
verificm mrimea programului iniial.
;
;testMarime.asm
;
section .text
global _start
_start:
nop
mov eax,1
mov ebx,0
int 80h
Asamblai cu make i observai mrimea acestuia.
ls -l testMarime
-rwxr-xr-x 1 ubuntu users 992 2011-02-19 21:59 testMarime
Dimensiunea fiierului executabil este de 992 de octei. Adugm
programului 8 MB de octei neiniializai i obinem executabilul.
;
;testMarime.asm
;+8192 octei neiniializai
section .bss
buffer:
times 8192 resb 1
;se poate folosi buffer: resb 8192
section .text
global _start
_start:
nop
mov eax,1
mov ebx,0
int 80h

ls -l testMarime
-rwxr-xr-x 1 ubuntu users 1075 2011-02-19 22:05 testMarime
Aadar, am adugat un buffer de 8192 de octei, dar mrimea
programului a crescut cu numai 83 de octei. S testm mrimea programului la
care au fost adugai 8192 de octei iniializai cu 0.
;
;testMarime.asm

;+8192 octei iniializai cu caracterul 0


section .data
buffer:
times 8192 db '0'
section .text
global _start
_start:
nop
mov eax,1
mov ebx,0
int 80h

n urma procesului de asamblare i editare de legturi observm c


buffer-ul de 8192 de octei este adugat fiierului executabil.
ls -l testMarime
-rwxr-xr-x 1 ubuntu users 9271 2011-02-19 22:05 testMarime

5.2. Adresarea memoriei n modul protejat


Principala instruciune de care ne-am folosit pn acum a fost instruciunea
MOV. MOV necesit doi operanzi i are urmtoarea sintax:
mov destinaie,surs
n sintaxa Intel, operandul aflat lng numele instruciunii este operandul
destinaie. Datele sunt copiate de la surs la destinaie, iar operandul
surs rmne neschimbat. Observai c am evideniat cuvntul copiate. Din
nefericire, numele instruciunii, prin faptul c pronunia, i chiar aspectul, seamn
cu cel al cuvntului englezesc move, care nseamn a muta, neal pe muli. Datele
nu sunt mutate, ci copiate de la surs la destinaie. La final, vom avea aceeai
valoare n dou locuri, n surs i n destinaie.
Instruciunea MOV poate copia date cu capacitatea de un octet, un cuvnt
sau dublu cuvnt, ntre registrele procesorului sau ntre registre i memorie.
Atenie, MOV nu poate copia date ntre dou locaii de memorie. Pentru aceasta este
nevoie de un registru intermediar i de dou instruciuni MOV una copiaz
valoarea din memorie n registru, i alta parcurge traseul invers.
MOV nu e singura instruciune care respect regulile de mai sus. Aproape
fiecare instruciune din setul de instruciuni poate opera pe octet, cuvnt sau dublu
cuvnt, iar atunci cnd primete doi operanzi, acetia nu pot fi n acelai timp

locaii de memorie. Operaiile cu ambii operanzi n memorie sunt excluse, cu


excepia operaiilor pe iruri i a operaiilor cu stiva.

5.2.1. Adresare imediat i adresare la registre


Pn acum am ntlnit dou forme ale instruciunii MOV:
transfer de date ntre registre:
mov eax,ebx

;copiaz valoarea din registrul EBX n registrul EAX

introducere operand imediat n registru:

mov eax,1
mov ebx,0

;introducem imediatul 1 n registrul EAX


;introducem imediatul 0 n registrul EBX

Un imediat este o valoare specificat direct n corpul instruciunii. Acest


fenomen poart numele de adresare imediat. Modul de adresare specific
criteriul folosit la calculul adresei unui operand (modul n care sunt specificai
operanzii unei instruciuni). Numele de adresare imediat provine de la faptul c
operandul este prezent chiar n corpul instruciunii main. Procesorul nu trebuie s
extrag valoarea operandului separat, dintr-un registru sau dintr-o locaie de
memorie, imediatul este inclus n instruciunea extras i executat.
MOV EAX,EBX semnific faptul c adresa operandului surs este adresa
unui registru de uz general, adic operandul se gsete ntr-un registru. Acest mod
de adresare se numete adresare la registre.
n completarea regulilor deja enunate, adugm i faptul c dimensiunea
operanzilor surs i destinaie trebuie s corespund. De exemplu, un imediat de
16 bii nu poate fi introdus ntr-un registru de 8 bii. Instruciunea MOV
AL,0FA95H este ilegal. De asemenea, numai operandul surs poate fi un
imediat. Asamblorul sancioneaz toate greelile de acest fel. Din acest punct de
vedere asamblorul este un pedagog foarte exigent.
Transferul datelor ntre registre este un proces simplu. Nu se poate spune
acelai lucru despre transferul datelor ntre registre i memorie. Primul lucru de
care trebuie s inem cont este modul de reprezentare al adresei de memorie n
corpul instruciunii. Ne amintim c eticheta directivei de date reprezint adresa
locaiei de memorie la care este stocat valoarea. Aadar, o instruciune de forma
mov ebx, d ;introduce n registrul EBX adresa d
stocheaz n registrul EAX adresa d, nu valoarea adresat de aceasta. Dei

este util, de cele mai multe ori dorim s transferm valoarea, nu adresa. Pentru a
obine valoarea, numele etichetei trebuie introdus ntre paranteze ptrate.
mov eax,[d]
;introduce n registrul EAX valoarea adresat de d
mov ecx,[ebx] ;introduce n registrul ECX valoarea adresat de
registrul EBX
Al doilea lucru care trebuie luat n considerare la transferul de date ntre
memorie i registre este dimensiunea datelor transferate. Instruciunea de mai sus
extrage de la o adres de memorie un numr de bii de date. Numrul de bii de
date transferat de la adresa d n registrul EAX nu este specificat direct n corpul
instruciunii. Coninutul locaiei de memorie adresate de eticheta d poate avea
dimensiunea de un octet, un cuvnt, un dublu cuvnt, un quad, etc.. Programatorul
a presupus c valoarea adresat prin eticheta d este reprezentat pe 4 octei i a
folosit registrul EAX. Dac programatorul dorea s extrag din memorie 8 bii de
date folosea ca destinaie un registru de 8 bii, precum AL. Dimensiunea registrului
specific numrul de bii extrai din memorie, sau, altfel spus, dimensiunea datelor
este dedus pe baza registrului destinaie.
;
;MemReg.asm
;
section .data
b
db
55h
w
dw
0ABCDh
d
dd
12345678h
section .text
global _start
_start:
nop
;ncarc n eax adresa variabilei b
mov eax, b
;ncarc n ebx adresa variabilei w
mov ebx, w
;ncarc n ecx adresa variabilei d
mov ecx, d
;ncarc n SI valoarea 0CD55H (16 bii de date ncepnd cu adresa b)
mov si, [b]
;ncarc n DI valoarea 0ABCDH
mov di, [w]
;ncarc n EDX valoarea 12345678H
mov edx, [d]

;ncarc n EBP valoarea 5678abcdh (32 de bii de date ncepnd cu adresa w)


mov ebp, [w]
mov eax, 1
mov ebx, 0
int 80h
Transferul datelor din registre n memorie folosete o abordare similar.
mov [d],ecx ;ncarc la adresa d octeii aflai n ecx
Instruciunea copiaz 4 octei de date din registrul ECX n locaia de
memorie specificat de eticheta d. La fel ca nainte, instruciunea introduce n
memorie coninutul unui registru de 4 octei, aadar va folosi 4 octei de memorie.
Completai programul anterior cu urmtoarele instruciuni i rulai pas cu pas.
Observai efectele. Desenai noua hart a memoriei.
mov al, 13
mov [b], al
mov bx, 65535
mov [w], bx
mov ecx, 0aabbccddh
mov [d], ecx
;mov ebp, 0
;mov [b], ebp
Activai ultimele dou instruciuni i vizualizai efectele.
O directiv de date poate introduce n memorie mai mult de o singur valoare. De
exemplu, directiva
valori: dw 10,20,30,40,50,60
introduce n memorie o secven de valori, fiecare valoare ocup o unitate de
memorie egal cu 2 octei. Reprezentarea memoriei, conform conveniei littleendian, este dat mai jos. Presupunem c adresa de nceput, etichetat valori,
este 0x8049094.

Valoare
0
60
0
50
0
40
0
30
0
20
0
10
7

Adres
0x804909e
0x804909c
0x804909a
0x8049098
0x8049096
valori
0

Prima valoare se acceseaz simplu: adresa este precizat direct n


instruciune, prin intermediul etichetei valori. n schimb, extragerea altor valori
din vector, de exemplu, elementul 40, necesit un mod de adresare mai special, n
sensul c adresa fiecrei locaii de memorie se determin pe baza urmtoarelor
elemente:
adresa de nceput a vectorului, numit adres de baz (baz);
un deplasament (n vector), care se adun la adresa de baz (deplasament);
mrimea elementului de date numrul de octei pe care este reprezentat
elementul (scal);
un index, care determin elementul selectat (index). Indexul reprezint
poziia elementului n ir, numrnd de la zero.
Formatul complet al expresiei este:
[baz + index scal + deplasament]
EAX
EBX
ECX
EDX
ESI
EDI
EBP
ESP

EAX
EBX
ECX
EDX
ESI
EDI
EBP

1
2
4
8

fr deplasament
deplasament pe 8 bii
deplasament pe 32 de bii

Aadar, adresa locaiei de memorie corespunztoare valorii dorite se obine

adunnd baz + deplasament + index scal. Sunt valabile


urmtoarele reguli:
baza i indexul pot fi reprezentate de oricare din registrele de uz general de
32 de bii;
deplasamentul poate fi reprezentat de orice constant de 32 de bii (evident,
valoarea 0, dei legal, este inutil);
scala trebuie s fie una din valorile 1, 2, 4, sau 8 (valoarea 1 este legal, dar
nu aduce niciun beneficiu, aadar nu este precizat niciodat);
toate elementele expresiei sunt opionale i pot fi utilizate n aproape orice
combinaie;
nu pot fi utilizate registre de 16 sau 8 bii.
Tabelul 5.1 rezum metodele prin care poate fi specificat o adres de memorie
n modul protejat. Cu excepia primelor dou, pe care deja le-am ntlnit, toate
celelalte implic un mic calcul aritmetic ntre doi sau mai muli termeni, proces
numit calculul adresei efective. Procesul de calcul se finalizeaz cu obinerea
adresei efective. Termenul adres efectiv ilustreaz faptul c, n cele din urm,
aceasta este adresa care va fi folosit la citirea sau scrierea memoriei, indiferent de
modul n care este exprimat (format). Calculul adresei efective se face la
momentul execuiei instruciunii ce conine expresia.
Tabelul 5.1 Moduri de adresare a memoriei

Schem
[baz]
[deplasament]
[baz + deplasament]
[baz + index]
[index scal]
[index scal + deplasament]
[baz + index scal]
[baz + index scal +
deplasament]

Exemplu
[edx]
[etichet]
sau [0x8049094]
[ebx + 0FH]
[eax + ebx]
[ecx 2]
[eax 4 + 32]

Descriere
Numai baz
Deplasament, adres
simbolic sau explicit
Baz plus constant
Baz plus index
Index nmulit cu scal
Index nmulit cu scal plus
deplasament
[ebp + edi 2]
Baz plus index nmulit cu
scal
[esi + ebp 4 + 1] Baz plus index mnulit cu
scal, plus deplasament

Adresarea indirect
Primul caz presupune c adresa efectiv este reprezentat de valoarea unui
registru de uz general aflat ntre paranteze ptrate. Am vzut deja c, pe lng date,
registrele pot conine adrese de memorie. Registrul care conine o adres de

memorie se numete indicator (pointer). Accesarea datelor stocate n locaia de


memorie indicat de adresa din registru se numete adresare indirect. Poate fi
folosit oricare din registrele EAX, EBX, ECX, EDX, EBP, EDI sau ESI.
Modul n care stocm o adres ntr-un registru a fost ntlnit n programul
memReg.asm. Aadar, instruciunea
mov ecx,valori
va avea ca rezultat stocarea adresei 0x8049094 n registrul ECX. Cum
adresele IA-32 sunt reprezentate pe 32 de bii, registrul trebuie s fie unul de 32 de
bii. Programul care urmeaz stocheaz n registrul AL primul octet din vectorul
valori.
;
;indirecta.asm
;
section .data
valori:
dw
10,20,30,40,50,60
section .text
global _start
_start:
nop
mov ecx, valori
mov al, [ecx]
;AL = 0ah (adresare indirect)
;mov bl, 255
;BL = 0ffH
;mov [ecx], bl ;primul octet din vector devine 0ffH
mov eax,1
mov ebx,0
int 080h
Instruciunile comentate introduc n memorie, la adresa de nceput a
vectorului, valoarea 255. n locul celor dou ncercai s folosii instruciunea
mov [ecx], 255
Asamblorul va semnala o eroare:
error: invalid size for operand 1
Ceea ce asamblorul ncearc s ne spun este c nu poate determina dimensiunea
locaiei de memorie la care trebuie s introduc valoarea 255. I s-a specificat

adresa, dar nu i numrul de octei. Acesta este rolul operatorilor de dimensiune.


YASM pune la dispoziie 5 operatori de dimensiune: BYTE, WORD,
DWORD, QWORD, TWORD. Acetia sunt folosii atunci cnd exist ambiguiti cu
privire la numrul de octei pe care trebuie reprezentat n memorie un imediat. n
cazul nostru, nu reiese clar dac introducem n memorie un octet, un cuvnt, etc..
Operator de dimensiune
BYTE
WORD
DWORD
QWORD
TWORD

Octei adresai
1
2
4
8
10

Pentru o valoare de 255, un octet este suficient:


mov [ecx], byte 255
Aa cum reiese din intruciuni, chiar dac adresarea indirect nu necesit
vreun calcul al adresei efective, adresa nu apare n clar i nici codificat simbolic.
De fapt, singura form sub care este posibil ca adresa efectiv s apar n clar este
aceea de adres explicit scris ntre paranteze ptrate:
mov eax, [0x8049094]
Acest mod de adresare direct este aproape imposibil, deoarece n
momentul asamblrii este extrem de improbabil s cunoti adresa exact. Aproape
ntotdeauna vei cunoate codificarea simbolic a adresei (eticheta). Sistemul de
operare mapeaz eticheta la adres n momentul ncrcrii programului n
memorie.
Adresarea direct prin deplasament
Acesta a fost primul mod de adresare al memoriei ntlnit n aceast carte.
Adresa efectiv a operandului se obine din cmpul deplasament. S ne amintim:
mov [b], al
mov [w], bx
n acelai mod,
mov bx, [valori]

introduce n registrul BX valoarea 10. Principala caracteristic a termenului


deplasament este c nu se afl ntr-un registru.
Un caz particular al acestui mod de adresare se ntlnete atunci cnd
adunm la deplasament un numr ntreg.
mov ax, [valori + 2]
Pare c adresa efectiv se formeaz prin adunarea a dou deplasamente.
ns YASM, la momentul asamblrii, combin cele dou valori, sau mai multe,
ntr-un singur deplasament. n cazul prezentat, pur i simplu adaug 2 la adresa
simbolic valori. Adresa obinut va fi 0x8049094 + 2 = 0x8049096, adic
adresa primului octet al celui de al doilea element din vector. n registrul AL vom
avea valoarea 20. Am precizat ntr-un capitol precedent c putem accesa toate
valorile unui ir, sau vector, numai prin cunoaterea adresei de nceput. Trebuie
numai s inem cont de mrimea elementelor. Cum fiecare element din irul dat ca
exemplu are 2 octei, elementul 6 din vector poate fi extras cu instruciunea
mov ax, [valori + 10]
Elementul 6 este numrul 60.
;
;directa.asm
;
section .data
valori:
dw
10,20,30,40,50,60
section .text
global _start
_start:
nop
mov di, [valori]
mov ax, [valori+2]
;copiaz n ax elementul 2 (20)
mov bx, [valori+4]
;copiaz n bx elementul 3 (30)
mov cx, [valori+6]
;copiaz n cx elementul 4 (40)
mov dx, [valori+8]
;copiaz n dx elementul 5 (50)
mov si, [valori+10]
;copiaz n si elementul 6 (60)
mov eax,1
mov ebx,0
int 080h
S considerm instruciunea:

mov [d], 1 ;valid, adresare direct prin deplasament


n momentul asamblrii, YASM va afia o eroare:
operation size not specified
Asamblorul nu tie dac 1 nseamn 01H, 0001H sau 00000001H. Altfel spus, nu
cunoate dimensiunea lui 1. d este o etichet, o adres de memorie. O etichet nu
are tip, nu denot o dimensiune. De aceea, trebuie s specificm dimensiunea
operanzilor imediai.
mov dword [d], 1
;1 este reprezentat pe 4 octei
mov byte [b], 1
;1 este reprezentat pe 1 octet
mov [w], dword 'G'
;caracterul G este reprezentat pe doi
octei i introdus n locaia de memorie etichetat cu w.
Adresarea bazat (baz + deplasament)
O alt modalitate simpl de calcul a adresei efective este s adunm o
constant la coninutul unui registru.
;
;bazata.asm
;
section .data
valori:
dw
10,20,30,40,50,60
section .text
global _start
_start:
nop
mov ebx, valori
mov ax, [ebx+4]
;copiaz n AX elementul 3 (30)
mov cx, [ebx+6]
;copiaz n CX elementul 4 (40)
mov edx,2
mov bx,[valori+edx]
;copiaz n BX elementul 2 (20)
mov ax,[valori+edx+8] ;copiaz n AX elementul 5 (50)
mov eax,1
mov ebx,0
int 080h

Descrierea acestei metode de calcul, baz + deplasament, este confuz.


n majoritatea cazurilor, cuvntul deplasament denot o adres (gndii-v la
adresarea direct prin deplasament), n schimb, aici are rolul pur i simplu de o
constant (o deplasare fa de adresa de baz). Aceast constant poate fi
reprezentat de un ntreg sau de numele vectorului.
n programul bazata.asm, primul mod de acces la elementele vectorului
se face prin ncrcarea n EBX a adresei de baz i adunarea sa cu un indice
explicit.
Al doilea mod de acces necesit o scurt discuie. Din punct de vedere al
mecanismului de adresare, n EDX se afl o adres de baz, iar valori este o
constant de 32 de bii. La toat aceast construcie se poate aduga uor o alt
constant explicit (8). Aadar, din perspectiva mecanismului de adresare, exist
un termen baz (EDX) i un termen de deplasare (valori). ns, din
perspectiva programului care acceseaz elementele vectorului, semnificaia este
invers: valori este adresa de baz, iar EDX un indice (deplasament).
Adresarea indexat
Adresa efectiv se obine prin adunarea a dou registre de uz general, unul
baz i unul index.
;
;indexata.asm
;
section .data
valori:
db
10,20,30,40,50,60
section .text
global _start
_start:
nop
mov ebp, valori
mov ecx, 4
mov ax, [ebp+ecx]
;copiaz n AX elementul cu index 4 (50)
mov word [ebp+ecx], 70
;ncarc 70 n locaia cu index 4
mov eax,1
mov ebx,0
int 080h
Acest mod de adresare este utilizat de obicei pentru parcurgerea unui
vector octet cu octet. Registrul baz marcheaz nceputul vectorului, iar registrul
index, incrementat ntr-o bucl, selecteaz elementul. Denumirea index trebuie
neleas ca poziia elementului n vector, numrnd de la zero. Cuvntul vector

este un termen general pentru buffer sau tablou: o secven de elemente de date
aflate n memorie, toate de acelai tip i mrime.
Dar dac trebuie s parcurgem un vector ale crui elemente nu sunt
reprezentate pe un singur octet, ci pe cuvinte sau dublu cuvinte? Aici intr n scen
conceptul de scal. Urmtorul listing arat modul n care depanatorul
interpreteaz adresarea indexat n contextul formulei:
[baz + index scal + deplasament]
(gdb) disassemble _start
Dump of assembler code for function _start:
0x08048080 <+0>:
nop
=> 0x08048081 <+1>:
mov ebp,0x80490a4
0x08048086 <+6>:
mov ecx,0x4
0x0804808b <+11>:
mov ax,WORD PTR [ebp+ecx*1+0x0]
0x08048090 <+16>:
mov WORD PTR [ebp+ecx*1+0x0],0x46
0x08048097 <+23>:
mov eax,0x1
0x0804809c <+28>:
mov ebx,0x0
0x080480a1 <+33>:
int 0x80
End of assembler dump.
Registrul index este nmulit implicit cu un factor de scal. Pentru c nu
am specificat n instruciune multiplicatorul dorit, asamblorul a presupus c
factorul de scal este egal cu 1. Aadar, adresa efectiv este suma dintre baz i
produsul registrului index cu factorul de scal. n mod obinuit, factorul de scal
este egal cu mrimea elementelor individuale din vector. Dac vectorul const din
cuvinte de 2 octei, scala ar trebui s fie 2. Dac vectorul conine cuvinte de 4
octei, scala ar trebui s fie 4. Pentru elementele de tip quad, scala este 8.
S presupunem c avem un ir de 100 de elemente, fiecare de 4 octei, i c
dorim s extragem elementele 73, 84 i 98. Ct adunm la baz? n primul rnd,
deoarece elementele sunt cuvinte de 32 de bii, nseamn c factorul de scal este
4. Nu mai trebuie dect s specificm indexul elementelor dorite cu ajutorul
unui registru de uz general (altul dect cel folosit pentru baz). Amintii-v c
indexul ncepe de la 0. Aadar, primul element din vector are index 0, al doilea
element index 1, .a.m.d.. Programul exemplific i alt form de adresare
indexat, [index scal + deplasament].
;
;scala.asm
;
section .data
x
equ 1

y
equ 2
z
equ 3
buff:
times 80 dd x
times 10 dd y
times 10 dd z
section .text
global _start
_start:
nop

;80 de cuvinte de 32 de bii cu valoarea 1


;10 cuvinte de 32 de bii cu valoarea 2
;10 cuvinte de 32 de bii cu valoarea 3

;adresarea prin deplasament+index*scal


mov edi, 72
mov esi, [buff+edi*4]
;adresarea prin baz+index*scal
mov eax, buff
mov edi, 83
mov ebx, [eax+edi*4]
mov edi, 97
mov ecx, [eax+edi*4]
mov eax,1
mov ebx,0
int 080h
Adresarea bazat i indexat
Adresa efectiv se obine prin adunarea unui registru de baz cu un registru
index, plus o constant de 32 de bii.
Acesta este i ultimul mecanism de calcul al adresei efective amintit (din
cele 11 prezente la procesoarele 80386). n definitiv, deocamdat conteaz s
nelegei cum se calculeaz adresa efectiv, putei ignora numele metodelor.
Reinei c toate metodele folosesc ca punct de plecare aceeai formul.
Instruciunea LEA (Load Effective Address)
Adresa efectiv poate fi ncrcat ntr-un registru i prin instruciunea LEA.
Sintaxa acestei instruciuni este
lea registru,[expresie]

Instruciunea LEA calculeaz adresa efectiv prin evaluarea expresiei aflat


ntre parantezele ptrate (dat ca operand surs) i ncarc aceast adres ntr-un
registru de uz general, de 32 de bii, dat ca operand destinaie. Astfel, n loc de
instruciunea
mov ebx, valori
se poate folosi
lea ebx, [valori]
Diferena ntre cele dou instruciuni este c varianta cu LEA calculeaz
valoarea deplasamentului la rulare, n timp ce varianta cu MOV, la asamblare. Din
acest motiv, varianta cu MOV se folosete de fiecare dat cnd este posibil. Totui,
LEA este mai flexibil n ceea ce privete tipul operanzilor surs. De exemplu,
pentru a introduce n EBX adresa elementului al crui index se afl n registrul
ESI, putem scrie
lea ebx, [valori+ESI]
Echivalentul
mov ebx, valori+ESI
este ilegal, deoarece coninutul lui ESI este cunoscut de abia la rulare. Pe
de alt parte, ne amintim c, n memorie, intrrile individuale nu au etichet. n
consecin, nu pot fi accesate direct. LEA poate obine adresa efectiv a oricrui
octet individual din memorie.
Instruciunea LEA permite i realizarea rapid a unor operaii aritmetice,
fr a fi nevoie de utilizarea instruciunilor de adunare sau nmulire. De exemplu,
expresia aritmetic x = x + y 8, presupunnd c valoarea lui x se gsete n
registrul EAX i valoarea lui y n registrul EBX, poate fi calculat rapid prin
lea eax, [eax + ebx * 8]
Nu numai c operaia este efectuat mai rapid dect n cazul utilizrii
instruciunilor aritmetice dedicate, dar este i mai lizibil operaia efectuat se
deduce imediat. Nu are importan c rezultatul final nu reprezint o adres. LEA
nu ncearc s acceseze elementul stocat la adresa pe care o calculeaz. Pur i
simplu, efectueaz operaia matematic cuprins ntre paranteze i stocheaz
rezultatul n operandul destinaie (rezultatul poate reprezenta o adres sau nu).

5.3. Optimizarea accesului la memorie


Accesul la memorie este unul din cele mai lente procese ndeplinite de
procesor. Cnd programele au cerine ridicate de performa este bine s pstrm
datele n registre i s evitm accesul la memorie ct mai mult posibil. Cel mai
rapid mod de tratare a datelor este s le transferm ntre registre. Dac pstrarea
datelor n registre nu reprezint o opiune viabil (nu dispunem de un numr
suficient de registre), trebuie s ncercm s optimizm procesul de extragere a
acestora din memorie.
Am nvat s privim memoria ca ir liniar de octei. Cndva, memoria
chiar aa era organizat i adresat, dar pentru procesoarele familiei 80386 aceast
imagine nu se mai reflect n hardware. De fapt, dimensiunea unei locaii de
memorie de un singur octet este o ficiune ntreinut n beneficiul dezvoltatorilor
de programe. Dei memoria este forat s emuleze un cuvnt de 8 bii, cuvintele
propriu zise sunt de 32 de bii (patru octei) - dimensiunea magistralei de adrese.
Dar, i n acest caz, primii doi bii ai magistralei de adrese nu sunt prezeni. Liniile
magistralei de adrese sunt de la A31 la A2. A1 i A0 lipsesc. Din punct de vedere
hardware, un procesor 80386 adreseaz 2!" cuvinte de 32 de bii. Memoria de
2!" 8 bii este o ficiune. Din cauza acestui mod de organizare hardware,
procesorul citete sau scrie eficient locaiile de memorie organizate n anumite
blocuri specifice, ncepnd cu prima adres a segmentului de date. De exemplu, un
procesor de 32 de bii acceseaz cel mai repid datele poziionate la adrese cu primii
doi bii de 0 (cei inexisteni, liniile A0 i A1).
Datele aliniate sunt datele aflate la adrese de memorie pe care procesorul
le poate accesa ntr-un singur ciclu de citire sau scriere. Alinierea datelor nu
depinde numai de adresa, ci i de mrimea lor:
valorile de un octet sunt ntotdeauna aliniate;
valorile de doi octei sunt aliniate numai atunci cnd sunt localizate la
adrese pare (adrese multiple de 2) bitul cel mai puin semnificativ este 0.
valorile de patru octei sunt aliniate atunci cnd sunt poziionate la adrese
perfect divizibile cu patru primii doi bii mai puin semnificativi sunt 0.
valorile de 8 octei sunt aliniate atunci cnd sunt stocate la adrese multiplu
de opt primii trei bii mai puin semnificativi sunt 0. Aceast aliniere este
important pentru procesoarele cu magistrale de date de 64 de bii, precum
Pentium. La procesoarele 80386, deoarece magistrala de date are 32 de
bii, o valoare de 64 de bii va fi citit ntotdeauna n dou cicluri de citire
i alinierea la granie de 4 octei este suficient.
Dei procesoarele IA-32 pot folosi att date aliniate ct i date nealiniate,
modul de organizare al datelor prezentat mai sus, numit aliniere natural, permite
unui program s ruleze mai rapid. Mai mult, anumite caracteristici ale procesorului

pot fi ntrebuinate numai cu date aliniate. Aadar, unele instruciuni lucreaz mai
bine cu date aliniate, altele chiar necesit aliniere. Unele sisteme de operare
necesit structuri de date aliniate.
Pentru procesoarele care folosesc memoria cache efectele nealinierii sunt
ntructva mai reduse. n general, accesul la datele nealinitate aflate ntr-o linie
cache nu necesit cicluri adiionale. Totui, accesul la datele nealiniate ntre liniile
de cache includ penalizare de vitez. n plus, accesarea memoriei n ordine
secvenial ajut la creterea probabilitii de cache hit, deoarece mai multe
blocuri de memorie vor fi citite n acelai timp n cache.
S judecm urmtorul caz: presupunem c lucrm cu date de 32 de bii
aliniate (aflate la adrese perfect divizibile cu patru). Procesorul va extrage datele n
grupuri de patru octei (deoarece sunt definite ca DD), de la adrese divizibile cu
patru (tot deoarece sunt definite ca DD).
Datele noastre sunt 1111 2222 3333 4444, aranjate n memorie astfel:
Tabelul 5.2 Date de 32 de bii aliniate

11
33

Valoare
11
22
33
44

22
44

Adres
..00
..00

Pentru a extrage cuvntul de 32 de bii 3333 4444, procesorul trebuie s


afle adresa divizibil cu patru i s extrag o singur dat. Dac datele sunt aranjate
n memorie conform Tabelului 5.3, ca s extrag aceleai date, procesorul trebuie:
s extrag primul cuvnt de 32 de bii i s preia trei octei din stnga (33
4444),
s extrag al doilea cuvnt de 32 de bii i s preia ultimul octet din dreapta
(33),
s pun octeii cap la cap.
Aadar, dou extrageri.
Tabelul 5.3 Date pe 32 de bii nealiniate

11
33

Valoare
22
22
44
44

33
11

Adres
..00
..00

Pe lng cele menionate, precizm i urmtoarele reguli de aliniere:


n segmentul de cod este indicat alinierea etichetelor accesate frecvent
(deoarece s-a observat o cretere a vitezei programului). n plus, unele
instruciuni necesit alinieri la 32 de octei.

n segmentul de date este necesar cel puin alinierea datelor la adrese


divizibile cu patru, altfel procesorul citete de dou ori ca s extrag o
singur dat, ceea ce penalizeaz serios performana.

O regul general spune c trebuie s definim nti datele cu dimensiuni


mai mari cuvintele de 32 de bii naintea celor de 16, cuvintele de 16 bii naintea
octeilor, etc.. Elemente de date de acelai tip trebuie plasate mpreun, la nceputul
seciunii de date. Acest lucru asigura alinierea corect. Dac se lucreaz cu iruri
sau buffere, este indicat, ori ca acestea s fie poziionate la sfritul seciunii de
date, astfel nct s nu strice alinierea altor elemente, ori s aliniem explicit datele
declarate dup ele.
Alinierea datelor n YASM
n YASM, alinierea datelor sau codului la cuvnt, la dublu cuvnt, la
paragraf (16 octei), etc., se face prin directivele align i alignb. Sintaxa este:
align
align
align
align
align
alignb

4
;aliniaz datele la frontiere de 4 octei
16
;aliniaz datele la frontiere de 16 octei
16,nop
;linie echivalent celei precedente
8, db 0 ;vezi OBS. II de mai jos
4, resb 1 ;aliniaz la frontiere de 4 octei n segmentul BSS
4
;linie echivalent celei precedente

Obs. I. Pentru ambele directive, argumentul trebuie s fie putere a lui doi (2, 4, 8,
16, 32).
Obs. II. Iniial, octeii liberi sunt completai cu valoarea 0x90 (codul main al
instruciunii NOP). Prin opiunea db 0, octeii liberi se completeaz cu 0.
Datele programului alignData.asm sunt nealiniate (directiva align 4
este comentat). Dup sir, urmeaz imediat d1 i d2:
;
;alignData.asm
;
section .data
sir
db
10,20,30,40,50,60
;align 4
d1
dd
0ffffffffh
d2
dd
0aaaaaaaah
section .text
global _start
_start:

nop
mov eax,1
mov ebx,0
int 080h
Dac urmrim ce se ntmpl n segmentul de date, observm:
Valoare
0xaa
0xaa
0xaa
0xaa
0xff
0xff
0xff
0xff
0x3c
0x32
0x28
0x1e
0x14
0x0a
7

Adres
0x804909d
0x804909c
0x804909b
0x804909a
0x8049099
0x8049098
0x8049097
0x8049096
0x8049095
0x8049094
0x8049093
0x8049092
0x8049091
0x8049090

Ultimul digit
...1110
...1100
...1011
...1010
...1001
...1000
...0111
...0110
...0101
...0100
...0011
...0010
...0001
...0000

Dac aliniem datele prin directiva align 4 (urmeaz date de tip DD),
obinem:
Valoare
Adres
Ultimul digit
0xaa
0x804909d
...1110
0xaa
0x804909c
...1100
0xff
0x804909b
...1011
0xff
0x804909a
...1010
0xff
0x8049099
...1001
0xff
0x8049098
...1000
0x90
0x8049097
...0111
0x90
0x8049096
...0110
0x3c
0x8049095
...0101
0x32
0x8049094
...0100
0x28
0x8049093
...0011
0x1e
0x8049092
...0010
0x14
0x8049091
...0001

0x0a
7

0x8049090
0

...0000

6. ARHITECTURA SETULUI DE
INSTRUCIUNI

Arhitectura setului de instruciuni (ISA Instruction Set Architecture)


cuprinde setul de instruciuni la nivel main recunoscut de procesor, tipurile de
date ce pot fi manipulate cu aceste instruciuni i contextul n care aceste
instruciuni opereaz. Arhitectura setului de instruciuni este o component a
sistemului de calcul vizibil programatorului n limbaj de asamblare, component
care reprezint interfaa ntre software i hardware. n acest capitol studiem
arhitectura setului de instruciuni specific procesoarelor Intel de 32 de bii.

6.1. Simboluri cheie


Instruciunea MOV primete ca operanzi trei tipuri de date: date aflate n
memorie (memory data), date aflate n registre (register data) i date imediate
(immediate data date specificate direct n corpul instruciunilor). De acum nainte
indicm natura operanzilor prin urmtoarele simboluri:
r, registru.
m, memorie.
i, imediat.
b, octet (byte).
2, 2 octei.
v, 32 de bii sau 16 bii (variable).
e, nseamn E cnd se lucreaz cu 32 de bii i dispare n logica de 16
bii (extended).
Aceste simboluri cheie au fost utilizate de Stephen Morse n cartea
80386/387 Architecture. Dei ele nu respect formatul oficial, sunt de un real ajutor
n explicarea codurilor main. Aadar, instruciunea MOV poate primi una din
formele urmtoare:
mov r, r

mov r, m
mov m, i

mov r, m
mov m, r

Instruciunea MOV nu transfer date din memorie n memorie.


Variantele de mai sus pot fi reduse la dou reprezentri:
mov r,rmi
mov m,ri
Cnd nu precizm capacitatea operanzilor nseamn c instruciunea poate
lucra cu oricare din cele trei variante (octet, cuvnt, dublu cuvnt). n cazul
transferului de date ntre operanzi trebuie s inem cont c nu putem transfera date
ntre operanzi de dimensiuni diferite. Muli sunt nclinai s copieze registre de
dimensiuni mai mici n registre de dimensiuni mai mari n virtutea faptului c
ncape. Numai c instruciuni de tipul
mov bx,al
vor produce o eroare de asamblare. Aceast instruciune ncearc s copieze 8 bii
din registrul AL n registrul BX. n schimb, trebuie copiat coninutul registrului AX
n registrul BX, ambele de aceeai dimensiune. Evident, trebuie s fii ateni ca nu
cumva valoarea lui AX s difere de valoarea lui AL (cnd AH nu este zero).
Simbolizm acest lucru prin forme de genul:
mov rv,rmv
mov rb,rmb
mov rmv,rv
Dac inem cont i de acest ultim aspect, variantele instruciunii MOV cresc
considerabil. Setul de instruciuni poate fi interpretat la dou niveluri: nivelul de
asamblare i nivelul main. Pentru programatorul n limbaj de asamblare,
procesorul are aproximativ o sut de instruciuni. Dar unei instruciuni n limbaj de
asamblare (de ex., MOV) i corespunde, de fapt, mai multe formate de instruciuni
main, n funcie de tipul i mrimea operandului. La nivel main exist
aproximativ trei sute de instruciuni. Instruciunile nivelului de asamblare
simplific viziunea programatorului asupra setului de instruciuni: programatorul
scrie instruciunea n limbaj de asamblare, asamblorul o examineaz i determin
instruciunea de nivel main ce trebuie generat.

6.2. Codificarea instruciunilor


Mecanismul prin care asamblorul genereaz instruciuni main este destul
de complex. Totui, instruciunile folosite de-a lungul exemplelor noastre pn n
prezent nu sunt foarte complicate. Codificarea acestora este ilustrat n Tabelul 6.1.
Majoritatea codului main este dat n hexazecimal, dar unii octei sunt reprezentai
n binar.
Tabelul 6.1 Cteva instruciuni main de 32 de bii

mov
mov
mov
mov

Primul octet
10111DDD
10110DDD
89
88

rv,iv
rb,ib
rmv,rv
rmb,rb

Al doilea octet

nc 4

11SSSDDD
11SSSDDD

Instruciunile procesorului 80386 sunt codificate pe un numr de octei


cuprins ntre 1 i 15 (Figura 6.1).
ModR/M

OPCODE
cod operaie

d w mod

1 0

76

Imediat

reg

r/m

543

210

nc 4 octei

Figura 6.1 Formatul general al unei instruciuni

Primul octet conine codul operaiei (pe 6 bii), adic tipul prelucrrilor ce
vor fi efectuate la execuia instruciuni (operaie aritmetic, logic, etc.). n cazul
nostru este vorba de transfer de date. Al doilea octet conine informaii cu privire la
registrele care conin operanzii, adresarea memoriei, etc..
Codul main pentru MOV r,r este format din doi octei - primul este
reprezentat n hexazecimal, al doilea n binar. Octetul binar ncorporeaz adresele
registrelor. SSS reprezint codul de trei bii pentru registrul surs. DDD
reprezint codul de trei bii pentru registrul destinaie. Dimensiunea
registrelor depinde de bitul 0 al primului octet. Acesta este notat cu w (word) i
codific lungimea operanzilor:
w=0, operand de tip octet;
w=1, operand de tip cuvnt; la 386, aceast valoare nseamn operand de
dimensiune complet (16/32 de bii n funcie de modul de lucru).
n aceste condiii, adresele registrelor sunt prezentate n Tabelul 6.2.

Tabelul 6.2 Adresele registrelor

Registru
Adres
000
001
010
011
100
101
110
111

Cuvnt
w=1
EAX
ECX
EDX
EBX
ESP
EBP
ESI
EDI

Octet
w=0
AL
CL
DL
BL
AH
CH
DH
BH

Tot n primul octet se afl bitul d (direction), care indic direcia


rezultatului operaiei:
d=0, cmpul REG indic operandul surs;
d=1, cmpul REG indic operandul destinaie.
REG se afl n al doilea octet al instruciunii, numit ModR/M (dac exist). Octetul
ModR/M poate lipsi. De exemplu, instruciunea MOV r,i nu prezint octetul
ModR/M. Nu este zero, ci lipsete. Valoarea imediatului este memorat n cei patru
octei adiionali (de la 1 la 4; n funcie de dimensiunea destinaiei, imediatul poate
fi reprezentat pe un 1, 2 sau 4 octei).
Considerm urmtoarele exemple:
1
2
3
4
5
6
7
8
9

B801000000
B89B000000
B800010000
B8FEFF0000
BB00000000
B10F
89C8
89D3
88CD

mov
mov
mov
mov
mov
mov
mov
mov
mov

eax,1
eax,155
eax,256
eax,65534
ebx,0
cl,15
eax,ecx
ebx,edx
ch,cl

Primul octet al instruciunii MOV EAX,1, B8, este codificat folosind codul
10111DDD, conform formatului MOV rv,iv. Biii DDD sunt completai cu
adresa registrului EAX, rezultnd codul binar 1011 1000 (B8H). Valoarea
imediatului, ntregul 1, este reprezentat imediat dup acest OPCODE, pe urmtorii
4 octei. Observai cum valoarea imediatului de 4 octei, 00000001H, este stocat

ca 01 00 00 00, conform criteriului little-endian (de aceea valoarea sa n


instruciunea main pare a fi ntoars). De exemplu, valoarea 155 n hexazecimal
este 9B; n consecin, a doua instruciune este codificat B89B000000. Verificai
faptul c ai neles mecanismul ncercnd s deducei instruciunile main pentru
urmtoarele trei linii.
Linia 6 prezint o instruciune de forma MOV rb,ib. Codificarea folosit
este 10110DDD. Deoarece adresa registrului CL este 001 i imediatul 15 n
hexazecimal este 0F, instruciunea main se formeaz ca B10F.
Linia 7 prezint o instruciune de forma MOV rv,rv, cu registrul EAX ca
destinaie (biii DDD = 000) i registrul ECX ca surs (biii SSS = 001). Octetul
OPCODE este 89H, iar forma binar a octetului ModR/M este 11SSSDDD. Pentru
octetul ModR/M, dac plasm adresele registrelor pe poziiile corespunztoare,
obinem 1100 1000 n binar (C8H). Rezult instruciunea main 89C8.

6.2.1. Spaiul codurilor operaionale


Codurile main utilizate n ultima seciune pot asambla o mare varietate
de instruciuni n limbaj de asamblare. Pe de alt parte, pe ansamblu, ele reprezint
numai o mic parte din setul de instruciuni Intel x86. n aceast seciune facem o
descriere general a setului de instruciuni main.
Tabelul 6.3 prezint informaiile oferite de primul octet al codului main.
Majoritatea intrrilor din tabel conin informaii cu privire la mnemonic i
operanzi. n cteva cazuri aceast informaie este specificat complet. De exemplu,
intrarea corespunztoare octetului 90 (rndul 9, coloana 0) reprezint instruciunea
NOP. n alte cazuri se precizeaz mnemonica i o clas de operanzi admii (descrii
prin simbolurile cunoscute).
Informaiile transmise de primul octet (OPCODE) pot fi mprite n
urmtoarele clase:
Octetul OPCODE exprim complet instruciunea. De exemplu, NOP
specific instruciunea NOP (No Operation), care, aa cum denot i
numele ei, nu realizeaz nicio prelucrare. NOP ocup un singur octet i
dureaz trei cicluri de ceas.
Octetul OPCODE determin tipul operaiei, dar nu specific operanzii. n
aceste cazuri, de cele mai multe ori, aceast informaie este furnizat de cel
de al doilea octet, ModR/M. De exemplu, dac primul octet este 89H,
intrarea corespunztoare din tabel este MOV rmv,rv. Pe lng faptul c
specific operaia MOV, simbolurile operand rmv,rv transmit informaie
cu privire la modul n care trebuie interpretat octetul ModR/M. Acest caz a
fost deja ntlnit.

Tabelul 6.3 Codurile setului de instruciuni IA-32 [13]

Octetul OPCODE nu specific o operaie, ci o categorie de operaii, iar


octetul ModR/M este folosit att s completeze informaia de prelucrare ct
i s furnizeze informaia cu privire la operanzi. De exemplu, dac primul
octet este C1, intrarea n tabel este Shift rmv,ib. n aceste cazuri este
nevoie de interpretarea complet a octetului ModR/M (Figura 6.2). Shift
nu este un cuvnt mnemonic. El este un indicator ctre Tabelul 6.4. n
Tabelul 6.3, acest lucru este indicat prin faptul c nu toate literele
cuvntului sunt majuscule.
Dac primul octet este 0F, atunci se deschide o ntreag pleiad de
posibiliti. Informaia transmis de 0F este mergi la Tabelul 6.5.
Dac valoarea primului octet se afl n gama D8-DF, atunci este vorba de o
instruciune n virgul mobil.
Octei prefix. Octeii prefix modific comportamentul instruciunilor care i
urmeaz (de ex., LOCK).

6.2.2. Octetul ModR/M


Octetul ModR/M transmite informaii cu privire la operanzi n cazurile n
care primul octet este insuficient.
Mod
7

REG
4

R/M
1

Figura 6.2 Octetul ModR/M

Biii 7 i 6 sunt biii Mod. Cnd biii Mod sunt 11, cmpul R/M specific
un registru. n celelalte cazuri, biii R/M codific adrese de memorie.
Biii 5, 4, 3, formeaz cmpul REG, sau registru, i de cele mai multe ori
este desemnat prin notaia /r. Indic registrul care conine unul din
operanzi sau, mpreun cu cei 6 bii din primul octet, specific alte coduri
operaionale (Tabelul 6.4). Acest lucru nseamn c REG este determinat
de primul octet (OPCODE) al instruciunii.
Biii 2,1,0, formeaz cmpul R/M, sau registru/memorie. Dac Mod = 11,
atunci R/M indic registrul care conine al doilea operand. n toate celelalte
cazuri, R/M indic registrul implicat n aflarea locaiei de memorie ce
conine al doilea operand.
Tabelul 6.4 Instruciuni specificate de REG

/r
/0 000 /1 001 /2 010 /3 011
ADD
OR
ADC
SBB
Immed
ROL
ROR
RCL
RCR
Shift
NOT
NEG
Unary TEST i
INC
DEC
IncDec
INC
DEC CALL m CALL
Indir
FAR m

/4 100
AND
SHL
MUL

/5 101
SUB
SHR
IMUL

/6 110
XOR
SAR
DIV

JMP

JMP
FAR

PUSH

/7 111
CMP
IDIV

Unul din cazurile n care cei 6 bii de cod operaional prezeni n primul
octet sunt insuficieni pentru a defini complet operaia, i este necesar studierea
cmpului REG din octetul ModR/M, este cel al instruciunii AND ECX,64.
nregistrarea din Tabelul 6.3 pentru 83 este
Immed rmv,ib
Deoarece rndul intitulat Immed din Tabelul 6.4 are un AND sub /r = /4,
nseamn c instruciunea
AND rmv,ib

poate fi codificat folosind 83 ca OPCODE i 100 pentru biii REG din octetul
ModR/M. Din moment ce primul operand este registrul ECX, biii Mod trebuie s
fie 11 i R/M s conin 001 (adresa registrului ECX). Aadar, octetul ModR/M
este format din biii 11 100 001, sau E1H. ib nseamn imediat de tip octet valoarea sa se adaug la restul codului instruciune. n final, pentru AND ECX,64
se obine 83 E1 40.

6.2.3. Spaiul codurilor 386 (0F + ...)


La procesorul 8086 octetul 0FH codific instruciunea de extragere din
stiv a registrului CS. La urmtoarele procesoare aceast intruciune a fost
nlturat, astfel nct 0FH, acum disponibil, a permis o extindere semnificativ a
setului de instruciuni. Octetul 0FH se numete prefix de extindere a codurilor de
operaie. Tabelul 6.5 dezvluie spaiul nou creat.
Tabelul 6.5 Spaiul 386 [13]

Ca i Tabelul 6.3, Tabelul 6.5 prezint informaia transmis de un singur

octet - n acest caz, octetul care urmeaz octetului 0F. Majoritatea instruciunilor
din acest tabel au aprut pentru prima dat la procesorul 80386. Informaiile
furnizate de acest octet pot fi incluse n urmtoarele clase:
Specific complet o instruciune. De exemplu, 0F CA este codul pentru
BSWAP EDX.
Determin tipul operaiei, dar operanzii sunt specificai de octetul
ModR/M. De exemplu, instruciunea XADD (0F C1).
Determin o categorie de operaii, iar octetul ModR/M specific o anumit
operaie. De exemplu, dac primii doi octei ai instruciunii sunt 0F 01,
operaia este determinat de biii REG ai octetului ModR/M. Din Tabelul
6.6 reiese c, atunci cnd biii REG din ModR/M sunt 011, instruciunea
este LIDT.
Reprezint instruciuni MMX.
Tabelul 6.6 Instruciunile OF specificate de biii REG

/r

/0 000
SLDT
LocalT
rm2
SGDT
GlobalT
m6

/1 001
STR
rm2
SIDT
m6

/2 010
LLDT
rm2
LGDT
m6

/3 011
LTR
rm2
LIDT
m6

Bits

/4 100 /5 101 /6 110 /7 111


VERR
VERW
rm2
rm2
SMWS
LMSW
rm2
rm2
BT
BTS
BTR
BTC
rmv,ib rmv,ib rmv,ib rmv,ib

Instruciunile prezente n Tabelul 6.5 mai provin, pe lng procesorul


80386, de la 80486 i Pentium. De exemplu, XADD i BSWAP au aprut pentru
prima dat la procesoarele 80486, iar CPUID i RDTSC la Pentium.

6.2.4. Prefix de dimensiune operand


Pn acum am studiat numai instruciuni main care primesc drept
operanzi registre de 8 sau 32 de bii. n continuare studiem modul n care sunt
codificate instruciunile cu operanzi de 16 bii. Cteva exemple:
1
2
3
4

89D8
6689D8
B808000000
66B80800

mov
mov
mov
mov

eax,ebx
ax,bx
eax,8H
AX,8H

Instruciunea MOV EAX,EBX este codificat 89 D8. Echivalentul ei pe 16 bii


este MOV AX,BX. Surprinztor, codul pentru aceast comand este tot 89 D8

(ignorai primul octet). Procesorul 80386 a fost proiectat s foloseasc aceleai


codificri att pentru operaii cu registre de 32 de bii ct i pentru operaii cu
registre de 16 bii. Modul de operare implicit al instruciunilor, fie pe 16, fie pe 32
de bii, depinde de bitul D din descriptorul segmentului de cod. Bitul D specific
dimensiunea implicit att pentru operanzi ct i pentru adresa locaiilor de
memorie. D = 0 indic operaii pe 16 bii, D = 1 indic operaii pe 32 de bii. Bitul
D determin dac 89 D8 nseamn MOV EAX,EBX sau MOV AX,BX.
Acest lucru nu nseamn c instruciunea MOV AX,BX nu este disponibil
la procesoarele de 32 de bii. La aceste procesoare, asamblorul semnalizeaz
procesorului faptul c prezumia cu privire la numrul de bii setat implicit de
bitul D este greit prin adugarea prefixului 66 nainte de instruciune. Prefixul
66 este numit prefix de dimensiune operand. Astfel, pentru un mod de lucru
implicit pe 32 de bii (D = 1), codificarea 66 89 D8 nseamn MOV AX,BX.
Similar, pentru pentru un mod de lucru implicit pe 16 bii (D = 0), codul 66 89
D8 nseamn MOV EAX,EBX. Prefixul dimensiune operand indic faptul c
dimensiunea operandului nu este cea implicit. n mod implicit, sistemul Linux
seteaz bitul D (nu acelai lucru se poate spune de DOS), aadar pe sistemele
noastre 66 89 D8 nseamn MOV AX,BX.
Acum, odat ce am vzut cum aceeai codificare poate avea dou
semnificaii, ne este uor s explicm de la ce provine simbolul v din instruciunile
de genul MOV rmv,rv. Simbolul v din fiecare operand nseamn variabil, unde
variabil se refer la faptul c dimensiunea operandului poate fi de 16 sau 32 de bii.
n mod asemntor, simbolul e (extended) din reprezentrile registrelor, de
exemplu eAX, indic faptul c, n codificarea de 32 de bii, ne referim la registrul
EAX, iar n codificarea de 16 bii, la registrul AX.

6.3. Codificarea adreselor de memorie


Toate intrrile din Tabelul 6.3 care includ simbolul m descriu instruciuni
de acces la memorie. Observm c toate instruciunile MOV, ADD, ADC, SUB, SBB,
OR, AND i XOR pot avea ca surs sau destinaie operanzi aflai n memorie.
Acetia pot fi adresai n diverse moduri. n capitolul precedent am discutat pe larg
formatul general de adresare a memoriei. Aspectul funcional
[baz + index scal + deplasament]
rescris structural, arat astfel:
[reg + reg scal + imediat]

unde,

scal este un factor de scal, care trebuie s fie 1, 2, 4 sau 8;


registrul multiplicat de factorul de scal este numit registru index;
cellalt registru este numit registru de baz;
imediatul este un numr pe 8, 16 sau 32 de bii.
Rezult o mare varietate de formate, majoritatea deja studiate,
[reg]
[imediat]
[reg+reg]
[reg+imediat]
[reg+reg+imediat]
[reg+scalreg]
[scalreg+imediat]

n care imediatul reprezint fie adresa complet a operandului (deplasamentul), fie


o constant implicat n adresare (o deplasare). Poate este greu de crezut c
instruciuni cu asemenea grad de complexitate sunt codificate uor de procesor.
Costul acestei complexiti, n termeni de dimensiune (lungimea instruciunii care
folosete formatul complet), este de un singur octet. Octetul este numit SIB (Scale
Index Base).

6.3.1. Octetul SIB


Prefix
OPCODE
ModR/M
SIB
instruciune
Pn la 4 prefixuri Cod
1 octet
1 octet
de cte un octet
operaional
(dac este (dac este
(opionale)
de 1, 2 sau 3 necesar)
necesar)
octei

Mod

Reg/Opcode

R/M

Deplasament
Deplasament de
1,2 sau 4
octei
(opional)

Scal

Index

Imediat
Date imediate
de 1,2 sau 4
octei
(opional)

Baz

Figura 6.3 Formatul instruciunilor IA-32

n englez, un joc de cuvinte definete SIB ca fiind sibling cu octetul


ModR/M, aluzie la faptul c prezena sa n corpul instruciunii este indicat de
ModR/M (numai n codificrile pentru care acesta este insuficient).
Octetul SIB conine:

factorul de scal, pe primii doi bii;


registrul index utilizat, pe urmtorii trei bii (ca registre index pot fi
utilizate 7 din cele 8 registre de uz general, mai puin registrul ESP);
registrul de baz utilizat, pe ultimii trei bii (ca registre de baz
pot fi utilizate toate registrele de uz general).

6.4. Formatul instruciunilor


Toate codificrile instruciunilor IA-32 sunt subseturi ale formatului de
instruciune general prezentat n Figura 6.3. Instruciunile constau din prefixe de
instruciune opionale (n orice ordine), unul sau doi octei de cod operaie (unele
din instruciunile SSE i SSE2 au trei octei de cod operaie), un identificator al
formatului de adres (dac este necesar) - format din octetul ModR/M i, uneori,
octetul SIB, un deplasament (dac este necesar), i un cmp de date imediate.

6.4.1. Prefixele de instruciune


Prefixele de instruciune sunt valori speciale de un octet care afecteaz
operaia instruciunii. Prefixele de instruciune sunt mprite n patru grupuri,
fiecare cu un set de coduri admisibil.

Prefixe de instruciune (de blocare sau repetare) (Grup1)


o F0H, LOCK (garanteaz c instruciunea va avea acces exclusiv la
toat memoria partajat, pe toat durata execuiei sale).
o F2H, REPNE/REPNZ (folosite numai n operaii pe iruri).
o F3H, REP sau REPE/REPZ (folosite numai n operaii pe iruri).
Prefixe de segment (Grup 2)
o 2EH, registrul CS.
o 36H, registrul SS.
o 3EH, registrul DS.
o 26H, registrul ES.
o 64H, registrul FS.
o 65H, registrul GS.
Prefixe de indiciu de ramificare (Grup 2)
o 2EH, ramificare inactiv (folosit numai cu instruciuni de salt
condiionat).
o 3EH, ramificare activ (folosit numai cu instruciuni de salt
condiionat).

66H, prefix de dimensiune operand (Grup 3)


67H, prefix de dimensiune adres (Grup 4)

Pentru orice instruciune se poate folosi un prefix din fiecare grup, n orice
ordine.
Prefixul LOCK determin activarea semnalului de magistral omonim pe
durata execuiei acelei instruciuni. Are ca efect interdicia de cedare a
magistralelor unui alt dispozitiv. ntr-un sistem multiprocesor, acest semnal poate fi
utilizat de operaii atomice18 pentru a obine acces exclusiv la memoria partajat.
Prefixele de repetare provoac repetarea unei instruciuni pentru fiecare
element al unui ir. Acestea pot fi utilizate numai cu instruciuni pe iruri: MOVS,
CMPS, SCAS, LODS, STOS, INS i OUTS (vor fi studiate ntr-un capitol viitor).
Prefixele de segment foreaz unitatea de management a memorie s
foloseasc registrul de segment specificat n loc de cel implicit.
Prefixele de indiciu ramificare permit unui program s indice procesorului
cea mai probabil cale urmat de o instruciune de salt. Aceste prefixe pot fi
utilizate numai cu instruciuni de salt condiionat. Au fost introduse n procesoarele
Pentium 4 i Intel Xeon ca parte din extensiile SSE2.
Prefixul de dimensiune operand modific dimensiunea implicit a datelor
(de la 32 la 16 bii, sau invers).
Prefixul de dimensiune adres modific dimensiunea implicit a adreselor
(de la adrese de 32 de bii comut la adrese de 16 bii, sau invers).

6.4.2. Modurile de adresare prin octetul ModR/M


Cmpul Mod din registrul ModR/M reprezint o codificare a modului de
calcul a adresei efective sau lungimea deplasamentului i, mpreun cu cmpul
R/M, este utilizat pentru a determina adresa efectiv. Pn acum ne-am concentrat
atenia asupra instruciunilor n care biii cmpului Mod sunt 11. n aceste situaii,
cmpul R/M specific un registru i utilizeaz aceeai codificare ca i cmpul
REG. Am putut obine coduri pentru instruciuni de genul MOV EAX,EDX,ce
folosesc adresarea la registre. Ca o scurt recapitulare, reamintim paii procesului.
Din Tabelul 6.3 am identificat codul operaiei ca fiind 89H. Ultimii doi bii ai
octetului OPCODE, d, respectiv w, dau informaii cu privire la destinaia
rezultatului i la dimensiunea operanzilor. Deoarece bitul d este zero, nseamn c
n cmpul REG se afl codul registrului surs, aadar 010 (EDX), iar n cmpul
18

O operaie atomic este indivizibil si nu poate fi ntrerupt; odat ce operaia ncepe nu


va fi oprit sau ntrerupt pn cnd nu este finalizat, i nici o alt operaie nu-i va lua
locul n timpul acesta.

R/M trebuie s se afle codul registrului destinaie, 000 (EAX). Valoarea coninut
n ModR/M se obine grupnd biii Mod REG R/M mpreun. Rezult 11 010 000
n binar, adic D0H. Instruciunea complet este codificat sub forma 89D0.
Tabelul 6.7 Formele de adresare pe 32 de bii cu ajutorul octetului ModR/M

Tabelul 6.7 face parte din manualul oficial pus la dispoziie de Intel pentru
dezvoltatorii de programe19 i prezint toate formele de adresare pe 32 de bii care
folosesc octetul ModR/M. Numerele hexazecimale reprezint valorile octetului
19

Intel Architecture Software Developer's Manual, Volume 2: Instruction Set Reference

ModR/M n aceste cazuri. Prima coloan arat modul de calcul al adresei efective,
a doua i a treia, setarea biilor Mod, respectiv R/M. Coloanele care urmeaz
prezint valoarea octetului ModR/M pe ansamblu, dar i adresa registrului care se
gsete n cmpul REG. Privii rndul corespunztor coloanei Mod = 11 (ultimul
rnd) i cutai codul D0. Prima coloan denot mecanismul de adresare la
registre, coloana R/M arat c n cmpul R/M se afl adresa registrului EAX, iar
coloana pe care se afl D0 arat c n cmpul REG se afl registrul EDX.
Dimensiunea registrelor a fost descifrat anterior cu ajutorul bitului w din octetul
codului de operaie. Considerm urmtorul exemplu
mov ah,ch
Din Tabelul 6.3 codul operaiei pentru o instruciune de genul MOV rmb,rb este
88. Valoarea 0 a bitului w din OPCODE (1000 1000) indic operaie la nivel de
octet. Valoarea 0 a bitului d (10001000) specific faptul c sursa (registrul CH) se
afl n REG i destinaia (registrul AH) n R/M. n concluzie, octetul ModR/M este
format din irul de bii 11 101 100, EC. Codul complet al instruciunii este 88EC.
n cazul
mov edx,ecx
codul instruciunii este 89CA. Dac n acest cod schimbm biii Mod de la 11 la
00, valoarea octetului ModR/M devine 0A (00 001 010). Instruciunea devine:
mov [edx],ecx
n acest caz, biii Mod indic un operand aflat n memorie. Codurile 01 i 10 sunt
utilizate la codificarea deplasamentelor imediate, precum MOV EDI,[EAX+5],
unde deplasamentul este de tip ib sau iv. Aadar, codul pentru MOV
EDI,[EAX+5] este 8B78 05. Codul pentru MOV EDI,[EAX+12345678H]
este 8BB8 78563412. Totui, dac aceast schem de codificare se folosea
uniform nu ar fi fost disponibil destul spaiu pentru formatele complexe ale
modurilor de adresare. Din acest motiv, codul pentru registrul ESP a fost
ndeprtat, poziia acestuia folosindu-se ca intrare ntr-un nou spaiu de codificare
ce folosete octetul SIB. n urmtoarele rnduri descriem pe larg posibilitile
introduse de octetul ModR/M.
Mod = 00 nseamn mod de adresare indirect (prin registre), direct prin
deplasament (R/M = 101) sau SIB fr deplasament (R/M = 100).
Din Tabelul 6.7 reiese c Mod = 00 nseamn adresare bazat. Cmpul R/M
specific un mod de adresare indirect sau bazat/indexat, mai puin pentru R/M =
101, care denot adresare direct prin deplasament, i R/M = 100, care indic ctre

octetul SIB. Slotul ocupat de modul de adresare direct prin deplasament aparinea
adresrii indirecte prin registrul EBP. Intel a decis c n locul acesteia,
programatorii pot utiliza adresarea indirect [EBP+ib], cu ib = 0 (dei
instruciunea este puin mai lung). Aadar, Mod = 00 poate fi folosit pentru
urmtoarele cazuri:
[reg]
[deplasament]
[deplasament + constant]
Mod = 01 nseamn c octetul (octeii) modului de adresare este urmat de
un deplasament (imediat) de 8 bii. Atenie, nu confundai deplasamentul cu
dimensiunea datelor. Dimensiunea datelor este specificat n octetul OPCODE prin
bitul w. Un deplasament de 8 bii nu nseamn date de 8 bii, ci o ajustare n
segment curpins n gama -128..+127. Cmpul deplasament din formatul general al
instruciunilor IA-32 va fi format dintr-un singur octet poziionat imediat dup
octeii OPCODE (rapiditate).
Mod = 10 nseamn c octetul (octeii) modului de adresare este urmat de
un deplasament de 32 de bii.

6.4.3. Modurile de adresare prin octetul SIB


Toate intrrile din Tabelul 6.7 definite prin intermediul cmpului Mod i
R/M = 100 trimit ctre octetul SIB i arat c instruciunea folosete o form de
adresare indexat.
[r32 + eax scal]
[imediat + r8 + eax scal]
[imediat + r32 + eax scal]
Aa cum se desprinde din Tabelul 6.8, octetul SIB specific registrul de
baz, registrul index i factorul de scal. Codul factorului de scal precizeaz
valoarea cu care va fi nmulit registrul index.

Tabelul 6.8 Formele de adresare pe 32 de bii cu ajutorul octetului SIB

Tabelul 6.9 Valoarea factorului de scal

Valoarea factorului de scal Valoarea (Registru index Factor de scal)


00
Index 1
01
Index 2
10
Index 4
11
Index 8

Pentru fiecare mod de adresare cu octet SIB, cmpul Mod din octetul
ModR/M specific dimensiunea deplasamentului (constant de deplasare). Poate fi
zero, unu sau patru octei.
Tabelul 6.10 Adresarea cu SIB

Mod
00
01
10

R/M
100
100
100

Mod de adresare
SIB
SIB + deplasament
SIB + deplasament

Niciun mod de adresare cu SIB nu permite utilizarea registrului ESP ca


registru index.
Un caz special apare cnd Mod = 00 i cmpul registrului de baz este 101.
Sunt adresrile de tip:
[deplasament + eax scal]

6.5. Studiu de caz


Primul listing prezint fiierul prog.lst generat la asamblare, urmtorul
reprezint dezasamblarea executabilului prog. n primul, codificrile instruciunilor
nu includ adresele de memorie la care se vor afla acestea n momentul rulrii
programului i este mai uor s descifrm codul operaional.
Listing 1.
cat prog.lst
2
3 00000000 0A000000140000001E4 00000000 0000002800000032005 00000000 00003C000000
6
7
8
9 00000000 90
10 00000001
B8[00000000]
11 00000006
8B1D[00000000]
12 0000000C
8B08
13 0000000E
8B5004
15 00000017
B803000000

[section .data]
valori: dd 10,20,30,40,50,60
[section .text]
[global _start]
_start:
nop
mov eax,valori
mov ebx,[valori]
mov ecx,[eax]
mov edx,[eax+4]
mov eax,3

16 0000001C
17 00000022
18 00000027
19 0000002A
20 0000002B
21 0000002D
22 0000002F
23 00000032
24 00000037
25
26 0000003B
27 00000040
28 00000045

8BB0[00000000]
BB[00000000]
8B0C83
41
6641
0409
83C009
05FF000000
6605FF00

mov esi,[valori+eax*1]
mov ebx,valori
mov ecx,[ebx+eax*4]
inc ecx
inc cx
add al,9
add eax,9
add eax,255
add ax,255

B801000000
BB00000000
CD80

mov eax,1
mov ebx,0
int 080

Listing 2.
objdump -d -M intel prog
prog:

file format elf32-i386

Disassembly of section .text:


08048080 <_start>:
8048080:
90
8048081:
b8 c8 90 04 08
8048086:
8b 1d c8 90 04 08
804808c:
8b 08
804808e:
8b 50 04
8048097:
b8 03 00 00 00
804809c:
8b b0 c8 90 04 08
80480a2:
bb c8 90 04 08
80480a7:
8b 0c 83
80480aa:
41
80480ab:
66 41
80480ad:
04 09
80480af:
83 c0 09
80480b2:
05 ff 00 00 00
80480b7:
66 05 ff 00
80480bb:
b8 01 00 00 00
80480c0:
bb 00 00 00 00
80480c5:
cd 80

nop
mov eax,0x80490c8
mov ebx,DWORD PTR ds:0x80490c8
mov ecx,DWORD PTR [eax]
mov edx,DWORD PTR [eax+0x4]
mov eax,0x3
mov esi,DWORD PTR [eax+0x80490c8]
mov ebx,0x80490c8
mov ecx,DWORD PTR [ebx+eax*4]
inc
ecx
inc
cx
add al,0x9
add eax,0x9
add eax,0xff
add ax,0xff
mov eax,0x1
mov ebx,0x0
int 0x80

Codificarea instruciunii MOV eAX,iv


Instruciunea MOV EAX,valori este codificat n acelai mod ca MOV
EAX,1 - ncrcarea unui imediat de 32 de bii n registrul de uz general EAX
(MOV eAX,iv). Diferena const din faptul c valoarea imediatului, 1, este
cunoscut la asamblare i asamblorul formeaz instruciunea complet. Observai
c valorile sunt scrise n little-endian imediat dup singurul octet de cod
operaional.
Instruciune: MOV eAX,iv
Codificare: B8 [00 00 00 00]
Binar: 1011 1000 [imediat]
Opcode = 10111000, indic ncrcarea n registrul EAX sau AX a unui
imediat de 32 sau 16 bii, n funcie de modul de lucru implicit. Deoarece,
n cazul nostru, modul de lucru implicit este pe 32 de bii, nseamn c
instruciunea se traduce: MOV EAX,imm32. Imediatul poate fi specificat
explicit sau simbolic (printr-o etichet).
Adresare direct prin deplasament
Instruciune: MOV EBX,[valori]
Codificare: 8b 1d [00 00 00 00]
Binar: 1000 1011 0001 1101 [depl32]
Opcode = 100010, indic o instruciune MOV.
d = 1, cmpul REG este destinaie.
w = 1, operanzi de 32 de bii.
REG = 011, registrul EBX.
combinaia Mod = 00 i R/M = 101 indic mod de adresare prin
deplasament (de 32 de bii). Aadar, dup instruciune va urma un astfel de
deplasament.
Deplasamentul este vizibil n al doilea listing i reprezint adresa de la care ncep
datele n memorie (0x80490c8 ). Registrul DS din faa deplasamentului arat c
datele se afl n segmentul de date implicit (adresat cu DS).

Tabelul 6.11 Registrele de segment implicite utilizate la adresare

Tip referin la memorie


Citire instruciune
Date
Operaii pe iruri
- ir surs
- ir destinaie
Operaii cu stiva
EBP utilizat ca registru de baz

Segment implicit
CS
DS

Deplasament
EIP
Adresa efectiv

DS
ES
SS
SS

ESI
EDI
ESP
Adresa efectiv

Adresare indirect prin registre


Instruciune: MOV ECX,[EAX]
Codificare: 8B 08
Binar: 1000 1011 0000 1000
Opcode = 100010, identific o instruciune MOV.
d = 1, cmpul REG este destinaie.
w = 1, operanzi de 32 de bii.
Mod = 00, adresare fr deplasament.
REG = 001, registrul ECX.
R/M = 000, indic utilizarea modului de adresare [EAX].
Adresare bazat (deplasament de 8 bii)
Instruciune: MOV EDX,[EAX+4]
Codificare: 8B 50 [04]
Binar: 1000 1011 0101 0000 [depl8]
Opcode = 100010, identific o instruciune MOV.
d = 1, cmpul REG este destinaie.
w = 1, operanzi de 32 de bii.
Mod = 01, cmpul deplasament are 8 bii.
REG = 010, registrul EDX.
R/M = 000, indic utilizarea modului de adresare [EAX+depl8].
Deplasamentul de 8 bii urmeaz imediat dup octeii de cod de operaie i ocup
un singur octet.

Adresare bazat (deplasament de 32 de bii)


Instruciune: MOV ESI,[valori+EAX*1]
Codificare: 8B B0 [00 00 00 00]
Binar: 1000 1011 1011 0000 [depl32]
Opcode = 100010, identific o instruciune MOV.
d = 1, cmpul REG este destinaie.
w = 1, operanzi de 32 de bii.
Mod = 10, cmpul deplasament are dimensiune complet. Deoarece modul
de lucru implicit este pe 32 de bii, deplasamentul are 32 de bii.
REG = 110, registrul ESI.
R/M = 000, indic utilizarea modului de adresare [ESI+depl32].
Deplasamentul de 32 de bii urmeaz imediat dup octeii de cod de operaie i
ocup patru octei.
Observai c asamblorul a interpretat ultimele dou instruciuni n acelai
mod. De fapt, prima va produce o eroare de tip Segmentation Fault,
programatorul a specificat o adres efectiv inexistent sau la care nu are drept de
acces.
Adresare indexat i scalat
Instruciune: MOV ECX,[EBX+EAX*4]
Codificare: 8B 0C 83
Binar: 1000 1011 0000 1100 1000 0011
Opcode = 100010, identific o instruciune MOV.
d = 1, cmpul REG este destinaie.
w = 1, operanzi de 32 de bii.
REG = 001, registrul ECX.
R/M = 100 mpreun cu Mod = 00, indic faptul c urmtorul octet este
SIB.
o SS = 10, registrul index se nmulete cu 4.
o Index = 000, registrul index este EAX.
o Baz = 011, registrul de baz este EBX.
Prefix de dimensiune operand
Ambele instruciuni, INC ECX i INC CX, sunt codificate conform

aceluiai criteriu, INC eCX. Programele de 32 de bii nu folosesc foarte des


operanzi de 16 bii, dar cnd folosesc, procesorul trebuie ntiinat. Acesta este
rolul prefixului de dimensiune operand, 66H. Programatorul nu trebuie s specifice
explicit acest lucru, imediat cum gsete n instruciune un operand de 16 bii
asamblorul adaug automat acest prefix. Totui, reinei c oricnd folosii operanzi
de 16 bii n programe de 32, dimensiunea instruciunii crete cu un octet. Totodat,
din cauza efectului acestora asupra mecanismului cache, execuia este mai lent.
Codificri alternative pentru instruciuni
Pentru unele instruciuni utilizate frecvent, cu scopul de a scurta codul
programului, Intel a creat codificri alternative (mai scurte). De exemplu, setul de
instruciuni Intel x86 ofer opcode de un singur octet pentru instruciuni de genul:
add al,constant
add eax,constant
Codurile de operaie sunt 04H i 05H. De asemenea, aceste instruciuni sunt cu un
octet mai mici dect instruciunile standard ADD imediat.
Chiar i instruciunea ADD ax,constant, care necesit un prefix de
dimensiune, este mai mic dect versiunea standard a lui ADD imediat. Un
compilator sau asamblor alege automat instruciunea cea mai scurt.
Intel ofer alternative de codificare numai pentru registrele acumulator:
AL, AX, EAX. Aadar, suntem ncurajai s utilizm aceste registre oricnd putem.

7. OPERAII CU NUMERE NTREGI

Numerele ntregi sunt numerele fr parte fracionar. Am fcut cunotin


cu ele nc din primul capitol al acestei cri, sau, mai degrab, avnd n vedere
c am lucrat numai cu ntregi fr semn, cu un tip al acestora. Numerele ntregi se
mpart n dou categorii: ntregi cu semn i ntregi fr semn. Acest capitol se
deschide cu o discuie referitoare la reprezentarea ntregilor cu semn.Un rol foarte
important n operaiile cu numere ntregi sunt indicatorii de stare. De aceea, tot
aici vom discuta pe larg modul n care indicatorii de stare afecteaz operaiile
aritmetice i logice. ncepem cu prezentarea instruciunilor care permit operaii
aritmetice i logice cu numere ntregi. nspre final abordm operaii mai
complexe: execuie condiionat, procesarea irurilor i lucrul cu stiva.

7.1. Reprezentarea numerelor cu semn


Problema numerelor cu semn const n reprezentarea semnului. Pe hrtie,
semnul unui numr, de exemplu -15, poate fi reprezentat printr-un simbol distinct:
1111. Deoarece calculatorul reprezint informaia numai ca iruri de 0 i 1, cel
mai uor pentru programatori a fost s considere bitul cel mai semnificativ ca fiind
bit de semn: 0 - numr pozitiv, 1 - numr negativ. Acest mecanism poart numele
de reprezentare cu bit de semn i magnitudine. Magnitudinea este format din
ceilali bii ai reprezentrii i furnizeaz valoarea propriu zis a numrului. ns,
aa cum vom vedea n paragrafele urmtoare, acest mod de reprezentare are
probleme de consisten. De aceea, n prezent, exist trei tehnici generale de
reprezentare a ntregilor cu semn:
magnitudine cu semn;
complement fa de unu;
complement fa de doi.
Pentru toate, bitul cel mai semnificativ al unei reprezentri poate fi
considerat bit de semn. Dar atenie la nuan, am spus poate fi considerat;
numerele negative sunt reprezentare diferit n cele trei metode de reprezentare.
Pentru c este foarte important, precizm de la nceput c arhitectura IA32 folosete metoda complementului fa de doi.

7.1.1. Reprezentarea cu bit de semn i magnitudine


n aceast reprezentare, numit i reprezentare n magnitudine cu semn, cel
mai semnificativ bit are rol de semn (1 pentru numere negative, 0 pentru numere
pozitive), iar biii rmai dau valoarea. Astfel, pentru numerele cu semn
reprezentate pe un octet, bitul 7 reprezint semnul, ceilali valoarea.
semn

magnitudine

Figura 7.1 Reprezentarea cu bit de semn i magnitudine

Cel mai mic numr este 1 1111111, adica 127, cel mai mare numr este 0 111
1111, adica +127. De unde rezult c domeniul de reprezentare al numerelor cu
semn pe un octet este -127 ..+127.
Se procedeaz similar pentru numerele reprezentate pe doi octei, pe patru
octei, etc.. Totui, la o privire mai atent, descoperim lucruri nu tocmai plcute.
De exemplu, valoarea zero are dou reprezentri distincte: 10000000 (-0) i
00000000 (+0). Lucru care complic unele operaii matematice. Mai mult,
operaiile aritmetice care folosesc reprezentarea cu bit de semn i magnitudine sunt
complexe. Daca adunm +1 cu 1, rezultatul este -2; rezultat fals (principiul de
adunare al numerelor binare este similar cu cel al numerelor zecimale: se adun
cifr cu cifr i se ine cont de transport):
0000 0001 +
1000 0001
------------1000 0010 = -2
Enumerm dezavantajele reprezentrii n magnitudine i bit de semn:
dou modaliti diferite de a reprezenta valoarea zero;
operaii aritmetice complexe;
numerele cu semn necesit instruciuni aritmetice diferite fa de cele ale
numerelor fr semn.

7.1.2. Reprezentarea n complement fa de unu


Numrul negativ este reprezentat ca fiind complementul fa de unu al
numrului pozitiv. Complementul fa de unu nseamn c se inverseaz numrul
pozitiv bit cu bit (1 devine 0 i 0 devine 1). Complementul fa de unu al numrului
0000 0001 este 1111 1110, al numrului 0101 1100 este 1010 0011, .a.m.d.
n aceast reprezentare, numrul -127 ar fi complementul fa de unu al

numrului 127. Cum 127 n binar este 0111 1111, complementul lui fa de unu, i
implicit numrul -127, va fi 1000 0000. Numrul -1 este complementul fa de unu
al irului binar 0000 0001, adic 1111 1110.
Dar i de aceast dat avem dou reprezentri diferite pentru valoarea 0:
0000 0000 (+0) i 1111 1111 (-0), cu efecte nedorite n efectuarea unor operaii
matematice. n plus, aritmetica n complement fa de unu este la fel de complicat.

7.1.3. Reprezentarea n complement fa de doi


Am specificat de la nceput c arhitectura Intel reprezint numerele ntregi
cu semn prin metoda complementului fa de doi. Aceast metod rezolv
problemele aritmetice ale reprezentrilor cu bit de semn i magnitudine sau
complement fa de unu printr-un simplu artificiu matematic: reprezentarea
negativ a unui numr se obine prin adunarea lui 1 la complementul fa de unu al
reprezentrii pozitive.
Asfel, -1 este reprezentarea n complement fa de unu a lui 0000 0001,
plus 1:
1111 1110 +
1
---------1111 1111
Aadar, -1 este reprezentat ca fiind irul de bii: 1111 1111.
-2, este complementul fa de unu al irului binar 0000 0010, plus 1:
1111 1101 +
1
---------1111 1110
Pe baza aceluiai principiu obinem:
ntreg cu semn Reprezentare
-3
1111 1101
...
- 127
1000 0001
- 128
1000 0000
...
+127
0111 1111
+126
0111 1110
...

2
1
0

0000 0010
0000 0001
0000 0000

Acest mecanism rezolv toate problemele care afecteaz modurile de


reprezentare discutate anterior. De exemplu,
-1 + 1 = 0
CF = 1

1111 1111 +
0000 0001
-----------0000 0000

Carry Flag (CF) este ignorat n aritmetica numerelor cu semn.


n concluzie:
avem o singur reprezentare pentru valoarea 0,
adunarea se realizeaz uor,
adunarea i scderea numerelor, cu sau fr semn, folosete aceleai
circuite hardware (aceleai ciruite logice).
Domeniul de reprezentare al numerelor cu semn este dat n Tabelul 7.1.
Tabelul 7.1 Domeniul de reprezentare al ntregilor cu semn

Bii de reprezentare
8
16
32

Plaj de valori
-128 ..+127
-32 768 ..+32 767
-2 147 483 648 ..+ 2 147 483 647

Nu trebuie s confundai numerele fr semn cu numerele pozitive cu


semn. De exemplu, domeniul de reprezentare pentru numere fr semn, de 32 de
bii, este 0 .. 4 294 967 295, iar domeniul numerelor pozitive cu semn de 32 de bii
este 0 ..+ 2 147 483 647. Reprezentarea ntregilor cu semn, ca i cea a ntregilor
fr semn, are natur circular. Pentru un anumit numr de bii, exist o grani la
care reprezentarea numerelor negative se continu cu cea a numerelor pozitive, sau
invers. De asemenea, observm c toate valorile negative ncep cu bit de 1 i toate
valorile pozitive cu bit de 0. n consecin, avem posibilitatea s deducem imediat
dac un numr este negativ sau pozitiv. Din aceast perspectiv, putem considera
bitul cel mai semnificativ ca fiind bit de semn. Reinei totui c acest bit nu este
bit de semn propriu-zis (n sensul reprezentrii cu bit semn i magnitudine), nu
semnific semnul exclusiv, ci intr n procesul de calcul al valorii. Altfel spus,
indic semnul, nu l reprezint.

Urmtorul program ne permite s studiem reprezentarea binar a ntregilor


cu i fr semn.
;
;intSemn.asm
;
section .data
b1
db
-127
;10000001
b2
db
127
;01111111
b3
db
-1
;11111111
w1
dw
-32768
;10000000 00000000
w2
dw
32767
;01111111 11111111
w3
dw
-1
;11111111 11111111
d1
dd
-1
;11111111 11111111 11111111 11111111
d2
dd
-45
;11111111 11111111 11111111 11010011
section .text
global _start
_start:
nop
mov al,[b1]
mov bl,[b2]
mov cl,[b3]
mov ax,[w1]
mov bx,[w2]
mov cx,[w3]
mov eax,[d1]
mov ebx,[d2]
mov eax,1
mov ebx,0
int 080h
Rulai programul prin intermediul depanatorului GDB. Identificai fiecare
valoare prezent n memorie.
(gdb) x /1bd &b1
0x80490c0 <b1>: -127
(gdb) x /1bt &b1
0x80490c0 <b1>: 10000001
(gdb) x /1hd &w1
0x80490c3 <w1>: -32768
(gdb) x /1ht &w1

0x80490c3 <w1>: 1000000000000000


Executai prima instruciune i afiai registrele AL i EAX prin comenzile
info reg al i info reg eax.
(gdb) i r al
al
0x81
(gdb) i r eax
eax
0x81

-127
129

Verificai reprezentarea binar:


(gdb) p /t $al
$2 = 10000001
(gdb) p /t $eax
$3 = 10000001
Reprezentarea n hexazecimal a irului 1000 0001 este n ambele cazuri 0x81, dar
valoarea zecimal difer. Depanatorul interpreteaz aceeai reprezentare binar ca
fiind -127 n registrul AL i 129 n registrul EAX. Nu este nicio eroare. Aceeai
reprezentare binar este interpretat diferit. De fapt, orice reprezentare binar a
ntregilor poate fi interpretat n dou moduri, cu semn i fr semn. Cnd judec o
valoare, depanatorul se ghideaz dup dimensiunea reprezentrii i valoarea
bitului de semn. Am specificat anterior c bitul cel mai semnificativ indic
semnul. n acest caz, instruciunea
mov al, [b1]
introduce n registrul AL valoarea binar 1000 0001, interpretat n zecimal
ca fiind 127, deoarece bitul cel mai semnificativ al octetului este 1. Depanatorul
judec la fel de corect i valoarea prezent n registrul EAX. Urmtoarea
reprezentare a registrului EAX ne ajut s nelegem mai uor fenomenul cu care
ne confruntm. Din cei patru octei prezeni n EAX, AL reprezint octetul mai
puin semnificativ.
0000000000000000
31

00000000
AH

10000001
AL
0

Dei depanatorul, la judecarea valorii din EAX, nu arat biii de 0 din faa
registrului AL, acetia sunt luai n considerare. Dac judecm din aceast
perspectiv valoarea zecimal existent n EAX, obinem chiar 129 (MSB = 0).

Ca regul general, valorile registrelor, atunci cnd sunt afiate n


zecimal, sunt judecate ca fiind cu semn.
Situaia discutat semnalizeaz un lucru extrem de util: are foarte mare
importan ceea ce se afl anterior n registre. Dac introducem o valoare n AL,
nu trebuie s considerm c registrul EAX are acea valoare.

7.2. Extinderea ntregilor


n seciunea precedent am vzut cum sunt judecate valorile din registre.
Cteodat avem nevoie s exindem valoarea unui ntreg (de la octet la cuvnt sau
de la cuvnt la dublu cuvnt).
Extinderea ntregilor fr semn
Dac rulm programul de mai jos
;
;faraSemn.asm
;
section .data
val
db
127
section .text
global _start
_start:
nop
mov eax,0ffffaaaah
mov al,[val]
;movzx eax, al
mov eax,1
mov ebx,0
int 080h
observm c valoarea din registrul EAX este eronat (presupunnd c
dorim s avem n EAX valoarea 127). Pentru a obine un rezultat corect, ar fi
trebuit n prealabil s iniializm registrul EAX cu zero. ns Intel a pus la
dispoziie o instruciune capabil s extind un ntreg fr semn, completnd cu
zero ceilali octei. Sintaxa instruciunii este

movzx destinaie,surs
unde sursa poate fi registru sau locaie de memorie de 8 sau 16 bii, iar
destinaia registru de 16 sau 32 de bii. Activai instruciunea MOVZX din
program i studiai efectul acesteia.
Extinderea ntregilor cu semn
;
;cuSemn.asm
;
section .data
val
db
-127
section .text
global _start
_start:
nop
mov al,[val]
movsx eax,al
mov eax,1
mov ebx,0
int 080h
n programul de mai sus, rezultatul este cel ateptat numai dup folosirea
instruciunii
movsx destinaie,surs
La fel ca n cazul instruciunii MOVZX, sursa poate fi registru sau locaie
de memorie de 8 sau 16 bii, iar destinaia registru de 16 sau 32 de bii.
Extinderea ntregilor cu semn este diferit de extinderea ntregilor fr semn. n
cazul numerelor negative, MOVSX completeaz biii de semn cu 1, nu cu 0. Biii de
zero ar schimba valoarea numerelor negative. De exemplu, octetul -1 (11111111)
ncrcat ntr-o locaie de tip cuvnt unde octetul superior este completat cu bii de
0, d 0000000011111111, care n notaia cu semn este +127, nu -1. Pentru a se
pstra valoarea unui ntreg cu semn, bii introdui trebuie s fie de acelai tip cu
bitul de semn. n acest caz, se obine valoarea 11111111111111. n notaia cu
semn, un ir de bii de 1 reprezent -1.
n cazul unui numr pozitiv, extinderea se face cu bii de 0. Modificai
valoarea lui val n 127.

7.3. Indicatori de stare


Indicatorii de stare au fost menionai fugitiv n seciunea dedicat
arhitecturii IA-32, cnd am discutat rolul registrul EFLAGS. Am afirmat la
momentul respectiv c fiecare bit din registrul EFLAGS are rol de sine stttor i
se numete indicator de stare. Un indicator de stare reprezint un singur bit de
informaie, a crui semnificaie este independent de oricare alt bit. Poziionat
iniial n 0, la apariia unui eveniment specific comut n 1, semnaliznd astfel o
anumit condiie prezent la nivelul procesorului. Un program poate testa condiia
respectiv i aciona n consecin. Totodat, prin modificarea unui indicator de
stare, programatorul poate seta un anumit comportament al procesorului.
ase din aceti indicatori de stare sunt folosii exclusiv pentru
monitorizarea condiiilor rezultate n urma operaiilor aritmetice i logice, sau
nrudite. Acetia sunt:
Indicatorul de zero (ZF Zero Flag);
Indicatorul de transport (CF Carry Flag);
Indicatorul de depire (OF Overflow Flag);
Indicatorul de semn (SF Sign Flag);
Indicatorul de transport la jumtate (AF Auxiliary Flag);
Indicatorul de paritate (PF Parity Flag).

7.3.1. Indicatorul de zero


Rolul indicatorului de zero este s indice dac rezultatul ultimei operaii
aritmetice sau logice este nul. Dac rezultatul este zero, indicatorul ZF se
poziioneaz automat n 1 (la prima vedere acest lucru este confuz, dar amintii-v
c indicatorii de stare sunt iniial poziionai n 0 i comut n 1 numai la apariia
unui eveniment). Reinei c nu coninutul unui registru modific indicatorul ZF, ci
numai rezultatul unei operaii aritmetice sau logice. De exemplu, o instruciune
care ncarc valoarea zero n acumulator
mov eax,0
nu trece ZF n 1, pentru c operaia de transfer nu este aritmetic sau logic. n
schimb, operaia de scdere afecteaz acest indicator. Posibilitatea de apariie a
unui rezultat nul n urma unei operaii de scdere este destul de evident, lucru care
nu se poate spune n cazul altor operaii. De exemplu, o adunare pe 8 bii sau
incrementarea/decrementarea unor valori nvecinate cu zero.

0000 1111 +
1111 0001
0000 0000

1111 1111 +
0000 0001
0000 0000

0000 0001 0000 0001


0000 0000

Indicatorul de zero este folosit n principal n cazuri de testare a egalitii unor


valori i numrare pn la o valoarea prestabilit. Din acest motiv, este folosit
extensiv de instruciunile de salt condiionat.

7.3.2. Indicatorul de transport


Indicatorul de transport este utilizat n operaiile aritmetice fr semn.
Indicatorul de transport semnalizeaz faptul c rezultatul unei operaii
aritmetice ntre numere fr semn a depit intervalul (prea mare sau prea mic)
corespunztor capacitii destinaiei (registru sau locaie de memorie). De exemplu,
atunci cnd se execut o adunare ntre dou numere fr semn, este posibil s
rezulte transport spre rangul superior, care depete dimensiunea registrului ce
conine rezultatul. n mod asemntor, la scderea unor numere fr semn, poate
aprea necesitatea unui mprumut de la rangul superior. n aceste cazuri,
indicatorul CF se poziioneaz n 1.
0100 1000 +
0000 0011
0100 1011
adunare fr transport(CF=0)

1000 1000 +
1000 0011
0000 1011

adunare cu transport(CF=1)

Indicatorul de transport este setat atunci cnd se depete capacitatea de


reprezentare a destinaiei n condiiile unei operaii cu ntregi fr semn.
Capacitate (bii)
8
16
32

Domeniu de reprezentare
0 .. 255
0 .. 65 535
0 .. 4 294 967 295

Orice operaie care genereaz un rezultat ce depete limitele acestor domenii de


reprezentare este semnalizat cu CF = 1. Este evident c orice rezultat negativ se
gsete n afara domeniului de reprezentare. De exemplu, BE BF = FF i CF = 1:

1011 1110 1011 1111


1111 1111

Instruciunile aritmetice pot opera cu date de 8, 16 sau 32 de bii. Dac sunt adunai
operanzi mai mari de 32 de bii, se nsumeaz pe rnd dou numere de 32 de bii.
Urmtorul exemplu ilustreaz cum putem aduna pe arhitecturi de 32 de bii dou
numere ntregi fr semn de 64 de bii (folosim reprezentarea hexazecimal):
1 transport din prima adunare
2610 15E8 1357 9AE7 +
59AC B341 FE70 5324
7FBC C92A 11C7 EE0B
Efectum dou operaii de adunare. nti adunm primii 32 de bii mai puin
semnificativi ai operanzilor. Se obine jumtatea inferioar a rezultatului. Totodat,
aceast operaie de adunare poate produce transport, lucru care seteaz indicatorul
de transport. A doua operaie nsumeaz urmtorii 32 de bii ai operanzilor
mpreun cu indicatorul de transport generat de prima adunare. Acest operaie
produce jumtatea superioar a rezultatului de 64 de bii.
n mod similar, adunarea a dou numere de 128 de bii implic un proces n
patru etape, n fiecare se adun cuvinte de 32 de bii.

7.3.3. Indicatorul de depire


Indicatorul de depire este utilizat n operaiile aritmetice cu semn.
Indicatorul de depire este echivalentul indicatorului de transport pentru
aritmetica numerelor cu semn. Rolul principal al indicatorului de depire este s
indice dac rezultatul unei operaii cu numere cu semn a depit domeniul de
reprezentare. Ne amintim c domeniul de reprezentare al numerelor cu semn pe 8,
16 i 32 de bii este:
Capacitate (bii)
8
16
32

Domeniu de reprezentare
-128 ..+127
-32 768 ..+32 767
-2 147 483 648 ..+2 147 483 647

Cnd se execut operaii aritmetice ntre operanzi cu semn, este posibil s


apar bit de transport ctre bitul de semn, ceea ce face ca rezultatul s fie eronat.
Indicatorul de depire semnalizeaz generarea unui transport de la bitul 6 ctre 7,

14 ctre 15, sau 30 ctre 31, n funcie de numrul de bii ai reprezentrii. Altfel
spus, OF semnalizeaz valorile interzise ale rezultatului n cazul operaiilor n
complement fa de doi. De exemplu, adunm +127 cu +127.
0111 1111 +
0111 1111
1111 1110
Numrul +127 este reprezentat pe un octet cu semn ca 0111 1111. MSB (Most
Significant Bit) este 0. Cnd adunm obinem rezultatul 1111 1110, rezultat eronat
n logica aritmeticii cu semn. MSB este 1 i nu 0, aadar 1111 1110 va fi interpretat
ca numr negativ, adic -2. n astfel de situaii, indicatorul de depire se
poziioneaz n 1.

7.3.4. Indicatorul de semn


SF indic semnul rezultatului unei operaii. Aadar, este util numai cnd
efectum operaii aritmetice ntre numere cu semn. Dac rezultatul ultimei operaii
este negativ, SF devine 1. Indicatorul de semn este copia valorii bitului de semn al
rezultatului unei operaii aritmetice.
Pe lng principala funcie a indicatorului de semn, care const n testarea
semnului pentru rezultatul generat de o operaie aritmetic, acesta se mai folosete
la implementarea buclelor de numrare, unde iteraiile sunt efectuate pn cnd
variabila de control este zero.
Din punctul de vedere al utilizatorului, semnul unui numr poate fi testat
printr-o instruciune de deplasare logic. Comparativ cu ceilali trei indicatori de
stare prezentai pn acum, indicatorul de semn este utilizat relativ rar n programe.
Totui, procesorul utilizeaz bitul de semn n cazul execuiei instruciunilor de salt
condiionat.

7.3.5. Indicatorul de transport la jumtate


Indicatorul de transport la jumtate basculeaz n 1 dac n cadrul unei
operaii aritmetice exist transport de la bitul trei la bitul patru (sau mprumut de la
bitul patru la bitul 3. De exemplu:
1
0000
1000
1001

transport
1000 +
1000
0000

1
0010
0101
1100

mprumut
1011 1100
1111

7.3.6. Indicatorul de paritate


Indicatorul de paritate se folosete de obicei la controlul de paritate,
metod elementar de detectare a erorilor ce pot aprea la transferul datelor pe
magistrale sau n comunicaiile seriale, etc.. Indicatorul PF se poziioneaz n 1
atunci cnd numrul biilor de 1 dintr-un cuvnt este par. De exemplu, dac
rezultatul unei operaii aritmetice este 0E3H, deoarece 1110 0011 conine un numr
impar de bii de 1, PF devine 0. Similar, rezultatul 33H va seta indicatorul de
paritate n 1, deoarece 0011 0011 conine un numr par (patru) de bii de 1.
Trebuie s reinem c descrierile anterioare sunt numai generalizri i
sunt influenate de modul de lucru impus de instruciunile individuale. Setarea
indicatorilor de stare variaz de la instruciune la instruciune. De exemplu, sunt
instruciuni aritmetice care pot produce o valoare de zero pentru rezultatul final,
dar care nu seteaz indicatorul de zero. Comportamentul unei instruciuni cu
privire la influenarea indicatorilor de stare trebuie verificat prin studierea
manualului de instruciuni.
Dou exemple elocvente sunt instruciunile INC i DEC. Acestea incrementeaz,
respectiv decrementeaz, un operand. Ambele folosesc un singur operand, care
poate fi registru sau locaie de memorie.
;
;incDec.asm
;
section .text
global _start
_start:
nop
mov eax, 0fffffffh
mov ebx, 0
inc eax
dec ebx
mov eax,1
mov ebx,0
int 80h
Studiai efectele instruciunilor de incrementare i decrementare. Comanda
info reg eflags afieaz registrul indicatorilor de stare.
Incrementarea registrului EAX transform valoarea acestuia n 0. n mod
normal, ar fi trebuit ca indicatorul de transport s fie poziionat n 1, dar nu este.

Indicatorul de transport nu este afectat de instruciunea INC.


Instruciunile INC i DEC nu influeneaz indicatorul de transport.
Motivele sunt dou:
Instruciunile INC i DEC sunt folosite n special la contorizarea
iteraiilor unor bucle. Cu 32 de bii, numrul de iteraii maxim este 4
294 967 295. Acest numr este destul de mare pentru majoritatea
aplicaiilor. n plus, dac acesta este depit,
deoarece INC i DEC modific valoarea numai cu valori de 1, condiia
detectat de indicatorul de transport este detectat i de indicatorul de
zero. De exemplu, presupunem c registrul ECX ajunge la valoare sa
maxim 4 294 967 295 (FFFFFFFFH). Dac executm INC ECX ne
ateptm ca indicatorul de transport s fie setat n 1. Totui, detectm
aceast condiie i prin faptul c ECX = 0, eveniment care seteaz
indicatorul de zero. n consecin, pentru aceste instruciuni,
modificarea indicatorului de transport este redundant.

7.4. Instruciuni de transfer condiionat


Instruciunile de transfer condiionat au aprut ncepnd cu familia de
procesoare P6 (Pentium Pro, Pentium II, i mai noi). O instruciune MOV
condiional (CMOV Conditional Move) este o instruciune MOV efectuat numai
dac sunt satisfcute anumite condiii. Sintaxa general este
CMOV<x> destinaie,surs
unde x reprezint una sau dou litere care specific condiia ce va declana
instruciunea MOV. Condiiile sunt bazate pe valorile curente din registrul
EFLAGS. Biii folosii de instruciunile MOV condiionale sunt CF, OF, PF, SF, ZF.
Instruciunile condiionale sunt mprite n:
instruciuni de transfer condiionat pentru operaii fr semn (la
determinarea diferenei ntre doi operanzi sunt folosii indicatorii CF, ZF,
PF).
Tabelul 7.2 Instruciuni MOV condiionale pentru operanzi fr semn

Instruciune
CMOVA/CMOVNBE
CMOVAE/CMOVNB
CMOVNC
CMOVB/CMOVNAE

Descriere
above/not below or equal
above or equal/not below
not carry
below/not above or equal

Condiie
(CF sau ZF) = 0
CF = 0
CF = 0
CF = 1

CMOVC
CMOVBE/CMOVNA
CMOVE/CMOVZ
CMOVNE/CMOVNZ
CMOVP/CMOVPE
CMOVNP/CMOVPO

carry
below or equal/not above
equal/zero
not equal/not zero
parity/parity even
not parity/parity odd

CF = 1
(CF sau ZF) = 1
ZF = 1
ZF = 0
PF = 1
PF = 0

Instruciuni de transfer condiionat pentru operanzi cu semn (la


determinarea condiiei folosesc indicatorii SF i OF).
Tabelul 7.3 Instruciuni MOV condiionale pentru operanzi cu semn

Instruciune
CMOVGE/CMOVNL
CMOVL/CMOVNGE
CMOVLE/CMOVNG
CMOVO
CMOVNO
CMOVS
CMOVNS

Descriere
greater or equal/not less
less/not greater or equal
less or equal/not greater
overflow
not overflow
sign (negative)
not sign

Condiie
(SF xor OF) = 0
(SF xor OF) = 1
((SF xor OF) or ZF) = 1
OF = 1
OF = 0
SF = 1
SF = 0

Instruciunile de transfer condiionat se gsesc n perechi, deoarece o


valoare val1 poate fi mai mare dect o valoarea val2 (CMOVA), dar la fel de bine
val1 nu este mai mic sau egal cu val2 (CMOVNBE).
;
;transfCond.asm
;
section .data
val dd
105,106,107,100,110,103
section .text
global _start
_start:
nop
mov ebx,[val]
mov edi,1
mov eax,[val+edi*4]
cmova ebx,eax
;dac EAX > EBX, atunci EBX = EAX = 106
inc edi
mov ecx,[val+edi*4]
cmovnbe ebx,ecx ;dac ECX ! EBX, atunci EBX = ECX = 107
mov eax,1

mov ebx,0
int 080h

7.5. Operaii aritmetice


Instruciunile aritmetice realizeaz operaiile aritmetice elementare
(adunare, scdere, nmulire, mprire). Fiecare instruciune afecteaz o parte din
indicatorii de stare SF, ZF, AF, CF, OF, numii din acest motiv i indicatori
aritmetici.

7.5.1. Instruciuni de adunare


Operaia de adunare a ntregilor se efectueaz cu instruciunea ADD.
add destinaie,surs
unde sursa poate fi un imediat, un registru sau locaie de memorie de 8,
16, 32 de bii, iar destinaia registru sau locaie de memorie de 8, 16 sau 32 de
bii. Rezultatul adunrii se pstreaz n destinaie. Indicatorii de stare sunt setai n
concordan cu rezultatul operaiei.
Observaii:
operanzii nu pot fi simultan locaii de memorie;
operanzii trebuie s aib aceeai dimensiune.
;
;adunare1.asm
;
section .text
global _start
_start:
nop
xor ax,ax
mov al,100
add al,[val]
movsx ecx,al ;numrul din AL este considerat cu semn
mov bx,50
add bx,45
add bx,-1
movsx ebx,bx ;instruciune redundant (explicai de ce)

add ebx,10
mov eax,1
mov ebx,0
int 080h
section .data
val
db
132
Indiferent de semnul ntregilor, instruciunea ADD execut adunarea corect,
ceea ce nseamn c poate fi folosit att pentru ntregi cu semn ct i pentru
ntregi fr semn.
Dac rulm programul de mai jos:
;
;adunare2.asm
;
section .text
global _start
_start:
nop
xor eax,eax
xor ebx,ebx
mov al,254
mov bl,1
add bl,al
mov eax,1
mov ebx,0
int 080h
n registrul BL vom avea rezultatul 255. ncrcm n BL valoarea 2 i
reasamblm. Cnd rulm pas cu pas suntem foarte ateni la registrul indicatorilor
de stare. naintea instruciunii de adunare ADD BL,AL, singurul indicator setat
este IF. n urma adunrii, rezultatul din BL este 0, iar registrul indicatorilor de stare
are setat CF. Indicatorul de transport semnalizeaz c a fost depit domeniul de
reprezentare al numerelor fr semn pe un octet. Dup cum tim, valoarea maxim
a unui numr fr semn pe un octet este 255. Rezultatul adunrii 254 + 2 este 256.
Indicatorul de transport a semnalat depirea capacitii registrului care trebuia s
conin rezultatul, adic apariia unui transport spre rangul superior.
Cnd lucrm cu ntregi fr semn, CF semnalizeaz faptul c rezultatul
adunrii a depit limita domeniului de reprezentare. Dac nu suntem siguri c

adunarea operanzilor se ncadreaz n domeniu trebuie ntotdeauna s verificm


indicatorul de transport.
n cazul ntregilor cu semn, indicatorul de transport este lipsit de
importan - acesta va fi setat ori de cte ori rezultatul este numr negativ (vezi
scderea), indiferent dac este valid sau nu. n schimb, atunci cnd folosim ntregi
cu semn, trebuie s inem evidena indicatorului de depire (OF). Introducei n
program urmtoarele instruciuni i observai efectul:
mov bl,2
mov al,127
add bl,al
n urma urma operairi de adunare ntre AL = 127 i BL = 2, indicatorul de
depire este setat, indicnd depirea limitei superioare a domeniului de
reprezentare pentru numere cu semn: [-128 ..+127].
Adunarea numerelor mai mari de 32 de bii
Instruciunea ADD permite adunarea operanzilor de 8, 16 sau 32 de bii.
Presupunem c dorim s adunm dou numere de 64 de bii fiecare:
401D0219D18D50E1H i 4016EDECE09C1528H. Deoarece sunt reprezentate
pe 64 de bii, nu le putem aduna direct (nu avem la dispoziie registre de 64 de
bii). De aceea, fiecare numr va fi reprezentat pe cte dou registre de 32 de bii.
EAX
401d0219
ECX
4016edec

EBX
d18d50e1
EDX
e09c1528

Mecanismul a fost prezentat n seciunea dedicat indicatorului de transport.


Presupune dou etape. n prima etap se adun cuvintele de 32 de bii din dreapta.
n a doua etap se adun cuvintele de 32 de bii din stnga, plus eventualul
transport generat de adunarea anterioar. Pentru acest lucru, pe lng instruciunea
ADD, Intel a pus la dispoziie i instruciunea ADC (Add with Carry).
adc destinaie,surs
unde destinaia i sursa respect aceleai reguli menionate la ADD. Numai
c, spre deosebire de instruciunea ADD, ADC adun la suma celor doi operanzi i
valoarea lui CF (0 sau 1).

Aadar, vom aduna registrele EBX i EDX, apoi registrele EAX i ECX,
rezultatul final regsindu-se n EAX:EBX. ncepem prin adunarea registrelor EBX,
EDX folosind ADD. Deoarece este posibil ca suma celor dou valori s depeasc
domeniul de reprezentare adic s fie setat CF, urmtoarele dou registre: EAX i
ECX, vor fi adunate cu ADC.
;
;add64bit.asm
;
section .data
alfa dq 0401d0219d18d50e1H
beta dq 04016edece09c1528H
section .bss
rez
resd 2
section .text
global _start
_start:
nop
mov ebx,[alfa]
mov eax,[alfa+4]
mov edx,[beta]
mov ecx,[beta+4]
add ebx,edx
adc eax,ecx
mov dword [rez],ebx
mov dword [rez+4],eax
mov eax,1
mov ebx,0
int 080h
Se ruleaz urmrindu-se evoluia CF dup prima adunare.
Adunarea unor valori de dimensiune diferit
Cnd adunm dou valori de dimensiuni diferite trebuie s fim ateni la
conversia acestora.
;
;adunare3.asm
;
section .data
b
db
100

w
dw
300
d
dd
65800
section .text
global _start
_start:
nop
mov al,[b]
mov bx,[w]
movsx ax,al
add bx,ax
mov ax,[w]
mov ecx,[d]
movsx eax,ax
add ecx,eax

;convertim octet la cuvnt

;convertim cuvnt la dublu cuvnt

mov eax,1
mov ebx,0
int 080h
Programul folosete instruciunea MOVSX. Alte instruciuni de extindere
sunt:

CBW (Convert Byte to Word) extinde AL la AX; fiecare bit din AH ia


valoarea celui mai semnificativ bit (bitul de semn) din AL.
CWD (Convert Word to Double word) extinde AX la DX:AX; fiecare bit
din DX ia valoarea MSB-ului din AX.
CWDE (Convert Word to Double EAX) extinde AX la EAX
CDQ (Convert Double word to Quad) extinde EAX la EDX:EAX; fiecare
bit din EDX ia valoarea MSB-ului din EAX.

Another thing I regret is that some of my well-chosen instruction mnemonics were


renamed when the instruction set was published. I still think it's catchier to call the
instruction SIGN-EXTEND, having the mnemonic of SEX, than to call it
CONVERT-BYTE-TO-WORD with the boring mnemonic CBW.
Stephen Morse
Toate aceste instruciuni extind o valoare mai mic la una mai mare prin
replicarea bitului de semn al valorii originare.
;
;intExt.asm

;
section .data
b1
db
100
b2
db
-100
w
dw
300
d1
dd
65800
d2
dd
-354059
section .text
global _start
_start:
nop
mov al,[b2]
cbw
mov al,[b1]
cbw
mov bx,[w]
add ax,bx

;AH = FFh - bitul de semn a lui AL


;AH = 00H - bitul de semn a lui AL

mov ax,[w]
cwde
mov ecx,[d1]
add eax,ecx
mov eax,[d1]
cdq

;EDX = 0000H - bitul de semn a lui EAX

mov eax,[d2]
cdq

;EDX = FFFFh - bitul de semn a lui EAX

mov eax,1
mov ebx,0
int 080h

7.5.2. Instruciuni de scdere


Instruciunea SUB scade sursa din destinaie, rezultatul fiind stocat n
destinaie:
sub destinaie,surs
unde sursa poate fi un imediat, un registru sau locaie de memorie de 8,

16, 32 de bii, iar destinaia registru sau locaie de memorie de 8, 16 sau 32 de


bii. Indicatorii de stare sunt setai n concordan cu rezultatul operaiei.
Observaii:
operanzii nu pot fi simultan locaii de memorie;
operanzii trebuie s aib aceeai dimensiune.
;
;scadere1.asm
;
section .text
global _start
_start:
nop
xor ax,ax
mov al,100
sub al,[val]
movsx ecx,al
;numrul din AL este considerat cu semn
mov bx,50
sub bx,45
sub bx,-1
movsx ebx,bx

;instruciune redundant (explicai de ce)

sub ebx,10
mov eax,1
mov ebx,0
int 080h
section .data
val
db
90
Instruciunea SUB execut corect scderea indiferent de semnul ntregilor,
ceea ce nseamn c poate fi folosit att pentru ntregi cu semn ct i pentru
ntregi fr semn.
Dac rulm programul urmtor
;
;scadere2.asm
;
section .text
global _start
_start:
nop

xor
xor
mov
mov
sub

eax,eax
ebx,ebx
al,4
bl,2
bl,al

mov eax,1
mov ebx,0
int 080h
n registrul BL vom avea rezultatul -2. Rulm pas cu pas i suntem foarte
ateni la registrul indicatorilor de stare. naintea instruciunii de scdere SUB
BL,AL, singurul indicator setat este IF. Dup scdere, rezultatul din BL devine 0
iar registrul indicatorilor de stare are setat CF. Indicatorul de transport
semnalizeaz c a fost depit domeniul de reprezentare al numerelor fr semn pe
un octet. Dup cum tim, valoarea minim a unui numr fr semn pe un octet este
0. Rezultatul scderii 2 4 este -2. Indicatorul de transport a semnalat depirea
limitei inferioare a domeniului de reprezentare a numerelor fr semn.
Cnd scdem dou numere fr semn, CF semnalizeaz trecerea
rezultatului sub valoarea zero. n cazul numerelor considerate cu semn, acest lucru
se ntmpl foarte des i CF este lipsit de importan. n schimb, trebuie s inem
cont de indicatorul OF.
S judecm urmtorul exemplu:
;
;scadere3.asm
;
section .text
global _start
_start:
nop
mov eax,7
mov ebx,3
sub ebx,eax
jc sfarsit
mov eax,1
int 080h
sfarsit:

mov eax,1
mov ebx,0
int 080h
Programul scade dou numere fr semn: 3 i 7 (ambele pozitive).
Deoarece rezultatul scderii este un numr negativ (3 7 = - 4), instruciunea de
salt va fi executat. La sfritul programului vom avea n EBX valoarea 0 (n cazul
unui rezultat pozitiv, saltul nu s-ar fi executat).
Cnd scdem dou numere fr semn, 3 i 7, un rezultatul mai mic de zero
este considerat invalid (pentru c limita minim de reprezentare a numerelor fr
semn este 0). Totui, n ciuda faptului c valorile se presupuneau a fi fr semn i
rezultatul invalid, procesorul trece rezultatul -4 n EBX. De ce? Deoarece
procesorul nu tie ce fel de numere gndim noi, cu sau fr semn, el ia n calcul
ambele variante i seteaz indicatorii de stare CF i OF n concordan. Programul
trebuie s determine dac valoarea este n afara domeniului de reprezentare al
ntregilor cu/fr semn. n cazul scderii unor ntregi fr semn, CF indic faptul c
aceasta s-a soldat cu rezultat negativ. n ce privete ntregii cu semn, din moment
ce rezultatul poate fi negativ i CF nu este semnificativ, trebuie s ne bazm pe OF,
care semnalizeaz depirea domeniului de reprezentare a numerelor cu semn.
n concluzie, procesorul nu tie dac numerele sunt cu semn sau fr
semn. El seteaz CF sau OF lund n calcul ambele posibiliti.
CF setat dac a fost depit domeniul de reprezentare al
numerelor fr semn.
o [0 ..255] pentru 8 bii
o [0 ..65535] pentru 16 bii
o [0 ..4294967295] pentru 32 de bii
n cazul unui octet putem reprezenta astfel:
CF = 1

CF = 1

fr semn
0

255

La adunarea numerelor fr semn, CF = 1 cnd rezultatul depete


valoarea maxim a reprezentrii folosite (255, 65535, etc.). La scderea numerelor
fr semn, CF = 1 cnd rezultatul este sub zero.

OF setat dac a fost depit domeniul de reprezentare al numerelor


cu semn.
o [-128 ..+127] pentru 8 bii
o [-32768 ..+32767] pentru 16 bii

[-2147483648 ..+ 2147483647] pentru 32 de bii

n cazul unui octet putem reprezenta astfel:


OF = 1

cu semn

- 128

OF = 1
+127

OF este setat atunci cnd adunm sau scdem doi ntregi de acelai semn i
se obine un rezultat de semn diferit.
Scderea numerelor mai mari de 32 de bii
Pentru a scdea dou numere de 64 de bii, mprim numerele n dou
pri de 32 de bii fiecare i efectum operaia de scdere pe rnd. La a doua
operaie se scade i valoarea lui CF. Instruciunea necesar se numete SBB
(Subtract with Borrow).
;
;sub64bit.asm
;
section .data
alfa dq 0401d0219d18d50e1h
beta dq 04016edece09c1528h
section .bss
rez
resd 2
section .text
global _start
_start:
nop
mov ebx,[alfa]
mov eax,[alfa+4]
mov edx,[beta]
mov ecx,[beta+4]
sub ebx,edx
sbb eax,ecx
mov dword [rez],ebx
mov dword [rez+4],eax
mov eax,1
mov ebx,0

int 080h
O instruciune nrudit cu SUB este instruciunea NEG. Instruciunea NEG
calculeaz complementul fa de doi al unei valori. Acelai rezultat se poate obine
prin scderea valorii respective din zero, cu instruciunea SUB. Instruciunea NEG
se execut ns mai rapid.

7.5.3. Instruciuni de comparare


Compararea a doi operanzi se efectueaz cu instruciunea CMP (CoMPare).
CMP realizeaz aceeai operaie ca instruciunea SUB, dar nu salveaz rezultatul.
Instruciunea
cmp destinaie,surs
simuleaz scderea destinaie surs, fr a genera rezultat. n schimb,
sunt modificai indicatorii de stare. Instruciunea se folosete pentru testarea unei
condiii:
dac destinaie > surs, rezult ZF=0, CF=0;
dac destinaie = surs, rezult ZF=1, CF=0;
dac destinaie < surs, rezult ZF=0, CF=1.

7.5.4. Incrementare i decrementare


Instruciunile INC i DEC au fost deja prezentate. Am vzut c sunt
folosite la incrementarea i decrementarea unui ntreg fr semn i nu afecteaz
indicatorul de transport. n acest fel se poate incrementa sau decrementa valoarea
unui contor fr afectarea operaiilor de adunare sau scdere din interiorul unei
bucle.
Formatul instruciunilor este:
inc destinaie
dec destinaie
unde destinaia poate fi registru sau locaie de memorie de 8, 16 sau 32 de
bii.
Instruciunile INC i DEC privesc valoarea din destinaie ca fiind ntreg
fr semn. Dac decrementm valoarea 0 reprezentat pe 32 de bii, noua valoare
va fi FFFFFFFF judecat 4294967295 (indicatorul de transport nu va fi setat) i

nu -1 (aa cum ar fi judecat din perspectiva numerelor cu semn). Atenie cnd


folosii aceste instruciuni asupra unor ntregi considerai ca fiind cu semn.

7.5.5. Instruciuni de nmulire


nmulirea numerelor fr semn se realizeaz cu instruciunea MUL.
mul surs
unde sursa este registru sau locaie de memorie de 8, 16 sau 32 de bii.
Cellalt operand al nmulirii i rezultatul au locaie implicit, dup cum urmeaz:
MUL r/m8, AL este multiplicat cu r/m8 i rezultatul este stocat n AX.
MUL r/m16, AX este multiplicat cu r/m16 i rezultatul este stocat n
DX:AX.
MUL r/m32, EAX este multiplicat cu r/m32 i rezultatul este stocat n
EDX:EAX.
Tabelul 7.4 Instruciunea de nmulire MUL

Surs
8 bii
16 bii
32 bii

Operand implicit
AL
AX
EAX

Rezultat
AX
DX:AX
EDX:EAX

nmulirea numerelor cu semn se realizeaz cu instruciunea IMUL. Spre


deosebire de instruciunea anterioar, ntlnim trei tipuri de instruciune IMUL:
1.

IMUL cu un singur operand. Are sintaxa identic cu instruciunea MUL.


imul surs
Tabelul 7.5 Instruciunea de nmulire IMUL cu un singur operand

Surs
8 bii
16 bii
32 bii

Operand implicit
AL
AX
EAX

Rezultat
AX
DX:AX
EDX:EAX

2. IMUL cu doi operanzi


imul destinaie,surs

cu urmtoarele cazuri:
imul
imul
imul
imul
imul
imul

r16,r/m16
r32,r/m32
r16,imm8
r16,imm16
r32,imm8
r32,imm32

Aceast variant nmulete cei doi operanzi i stocheaz rezultatul n


operandul destinaie.
3. IMUL cu trei operanzi
imul destinaie,surs,imediat
cu urmtoarele cazuri:
imul
imul
imul
imul

r16,r/m16,imm8
r16,r/m16,imm16
r32,r/m32,imm8
r32,r/m32,imm16

Aceast variant nmulete cei trei operanzi i stocheaz rezultatul n


operandul destinaie.
Observaii:
Instruciunea cu doi operanzi este o variant prescurtat a instruciunii
cu trei operanzi.
n cazurile 2 i 3 destinaia are acelai ordin de mrime ca sursa. n
cazul nmulirii este foarte ntlnit situaia n care rezultatul are numr
dublu de bii fa de operanzii nmulii. De aceea, trebuie s fim ateni
ca nmulirea celor doi, respectiv trei, operanzi s nu depeasc
capacitatea destinaiei (se verific cu ajutorul lui CF i OF).

7.5.6. Instruciuni de mprire


La mprire, dempritul va avea ntotdeauna un numr dublu de bii fa
de mpritor, iar n urma operaiei vom obine un ct i un rest, fiecare de mrimea
mpritorului.
mprirea numerelor fr semn se realizeaz cu instruciunea DIV.

div divizor
Tabelul 7.6 Instruciunea de mprire DIV

Demprit
AX
DX:AX
EDX:EAX

Divizor
8 bii
16 bii
32 bii

Ct
AL
AX
EAX

Rest
AH
DX
EDX

Numerele cu semn se mpart cu instruciunea IDIV, care respect n totalitate


sintaxa DIV.

7.6. Instruciuni de interschimbare a datelor


Cteodat este necesar s schimbm ntre ele valorile a dou registre. Un
dezavantaj al instruciunilor MOV este chiar faptul c nu poate interschimba valorile
a dou registre direct, fr a folosi un alt registru intermediar. De exemplu, pentru a
interschimba valorile registrelor EAX i EBX trebuie s folosim ca intermediar un
alt registru, sau o locaie de memorie.
mov ecx,eax
mov eax,ebx
mov ebx,ecx
Sunt necesare trei instruciuni, precum i un registru liber; consum semnificativ de
resurse pentru aceast operaie simpl. De aceea, setul de instruciuni cuprinde
cteva instruciuni care interschimb date fr intermediar. Cea mai simpl este
XCHG.
Instruciunea XCHG poate interschimba date ntre dou registre sau ntre un
registru i o locaie de memorie. Operanzii nu pot fi n acelai timp locaii de
memorie. Formatul instruciunii este:
xchg operand1,operand2
Instruciunea primete operanzi de 8, 16 sau 32 de bii, dar ntotdeauna de aceeai
dimensiune. Cu ajutorul acestei instruciuni, interschimbarea valorilor din registrele
EAX i EBX necesit o singur linie de cod:
xchg eax,ebx

Dac unul din operanzi se afl n memorie, procesorul activeaz n mod automat
semnalul LOCK (n cazul unei structuri multiprocesor, acest lucru asigur accesul
exclusiv la memorie pentru un singur procesor). Dei util n unele situaii mai
speciale, acest proces este mare consumator de timp i penalizeaz performana
programelor.
Instruciunea XCHG este foarte util n operaiile de sortare. De asemenea,
este la fel de util n interschimbarea octeilor unui cuvnt, procedur care
corespunde conversiei ntre formatele little-endian i big-endian. De exemplu,
xchg al,ah
convertete valoarea din registrul AX dintr-o form n cealalt. Gndii-v cum
putei converti dintr-un format n altul valoarea unui registru de 32 de bii. Nu este
o problem lipsit de semnificaie, n unele situaii ordinea octeilor are mare
importan. De exemplu, transferul datelor n reea se face conform conveniei bigendian. S facem un experiment. Am vzut n capitolele precedente c asamblorul
permite forme interesante de adresare imediat. De exemplu, urmtoarea
instruciune este perfect legal i nseamn introducerea unui ir de caractere n
registrul EAX:
mov eax,'ABCD'
Introducei instruciunea ntr-un program i afiai coninutul registrului
EAX imediat dup execuia acesteia:
(gdb) i r eax
eax
0x44434241

1145258561

31

15

'D' (44H)

'C' (43H)

'B' (42H)

'A' (41H)

Figura 7.2 Registrul EAX n reprezentare little-endian

n registrul EAX sunt caracterele ASCII: 'A' (41H), 'B' (42H), 'C' (43H) i 'D'
(44H). Deoarece caracterele ASCII sunt reprezentate pe 8 bii, cele patru elemente
ale irului ncap perfect n cei 32 de bii ai registrului. Chiar dac la prima vedere
pare c sunt introduse n ordine invers, acest lucru nu este adevrat. Ne aducem
aminte c arhitectura IA-32 lucreaz conform conveniei little-endian, care
stocheaz octetul mai puin semnificativ la adresa mai mic. Acest criteriu se aplic
i la registre. n registrul EAX, AL se afl pe poziia celui mai puin semnificativ
octet, urmat de AH i de ceilali doi octei. ntr-un ir de caractere, caracterul mai
puin semnificativ este cel din extrema stng. Aadar, 'A' este introdus n registrul

AL, 'B' n registrul AH, .a.m.d.. Rezultatul este cel menionat. Dac avei nc
dubii, introducei valoarea 'ABCD' n memorie i comparai ordinea octeilor.
Presupunem c trebuie s transmitem coninutul registrului EAX prin reea
i trebuie s-l convertim n format big-endian.
31

15

'A' (41H)

'B' (42H)

'C' (43H)

'D' (44H)

Figura 7.3 Registrul EAX n reprezentare big-endian

Operaia de conversie ntre little- i big-endian presupune interschimbarea octeilor


1 cu 4 i 2 cu 3. Este important de reinut c ordinea biilor din octeii individuali
nu se modific. Procesoarele Pentium au introdus o instruciune care efectueaz
aceast operaie. Formatul acesteia este:
bswap registru
Instruciunea BSWAP comut numai octeii unui registru de 32 de bii.
(gdb) i r eax
eax
0x41424344

1094861636

Urmtorul program exemplific comportamentul instruciunilor XCHG i


BSWAP.
;
;swapBig.asm
;
section .text
global _start
_start:
nop
mov bx,'AB'
xchg bl,bh
mov eax,'ABCD'
bswap eax
mov eax,1
mov ebx,0
int 80h
Alte instruciuni din aceast familie sunt prezentate n Tabelul 7.7.

Tabelul 7.7 Instruciuni de interschimbare

Instruciune
XCHG
BSWAP
XADD
CMPXCHG
CMPCHG8B

Descriere
Comut valori ntre dou registre sau ntre un registru i o locaie
de memorie.
Convertete octeii unui registru de 32 de bii din ordine littleendian n big-endian, sau invers.
Comut dou valori i stocheaz suma n operandul destinaie.
Compar acumulatorul cu o valoare extern i schimb operanzi
n funcie de rezultatul comparaiei.
Compar dou valori de 64 de bii fiecare i le schimb n funcie
de rezultatul comparaiei.

Instruciunea XADD comut valorile ntre dou registre sau ntre un registru
i o locaie de memorie, apoi adun valorile i stocheaz rezultatul n destinaie.
xadd destinaie,surs
unde sursa trebuie s fie obligatoriu un registru de 8, 16 sau 32 de bii, iar
destinaia registru sau locaie de memorie de dimensiunea corespunztoare.
Instruciunea XADD este disponibil ncepnd cu procesoarele 80486.
Instruciunea CMPXCHG compar operandul destinaie cu o valoare din AL,
AX sau EAX.
cmpxchg destinaie,surs
Dac valorile sunt egale, valoarea operandului surs se ncarc n destinaie. Dac
valorile sunt diferite, operandul destinaie este ncrcat n EAX, AX sau Al.
Operandul destinaie poate fi registru sau locaie de memorie de 8, 16 sau 32 de
bii. Operandul surs trebuie s fie un registru de dimensiune corespunztoare.
Instruciunea CMPXCHG este disponibil ncepnd cu procesoarele 80486.
;
;cmpxchg.asm
;
section .data
val dd
5
section .text
global _start
_start:
nop

mov eax,5
mov ebx,3
cmpxchg [val],ebx
mov eax,1
mov ebx,0
int 80h
Instruciunea CMPXCHG8B are acelai efect cu instruciunea CMPXCHG,
numai c lucreaz cu valori de 8 octei (de aici i 8B de la sfrit). Aceast
instruciune a aprut odat cu procesorul Pentium. Formatul instruciunii primete
un singur operand:
cmpxchg8b destinaie
Operandul destinaie adreseaz o locaie de memorie de 8 octei. Acetia sunt
comparai cu valoarea coninut n perechea de registre EDX:EAX (EDX registrul
superior i EAX registrul inferior). Dac valorile sunt egale, valoarea de 64 de bii
aflat n perechea de registre ECX:EBX este compiat n locaia de memorie
adresat de destinaie. Dac nu sunt egale, valoarea din locaia de memorie este
ncrcat n perechea de registre EDX:EAX.
;
;cmpxchg8b.asm
;
section .data
val dq
1122334455667788h
section .text
global _start
_start:
nop
mov edx,11223344h
mov eax,55667788h
mov ecx,ffffffffh
mov ebx,aaaaaaaah
cmpxchg8b [val]
mov eax,1
mov ebx,0
int 80h

7.7. Instruciuni de prelucrare la nivel de bit


Instruciunile de control din limbajele de nivel nalt, de selecie sau iteraie,
sunt implementate pe baza evalurii unor expresii logice sau booleene. Majoritatea
acestora sunt traduse n limbaj de asamblare ca operaii de manipulare a
operanzilor la nivel de bit. n setul de instruciuni exist trei grupuri de instruciuni
pentru manipularea biilor:
instruciuni logice;
instruciuni de deplasare;
instruciuni de rotaie.
Pe lng aceastea, exist i cteva instruciuni de testare i modificare sau scanare
pe bit.

7.7.1. Instruciuni logice


Operaiile logice pot avea ca rezultat o singur valoare (numit valoare de
adevr), din dou posibile: adevrat sau fals. Pentru reprezentarea acesteia
este suficient un singur bit. Din acest motiv, toate instruciunile logice discutate n
aceast seciune opereaz la nivel de bit. Prin convenie, valoarea logic fals este
asociat valorii 0, orice valoare diferit avnd semnificaia adevrat.
Exist patru operaii logice de baz: AND (conjucie), OR (disjuncie),
XOR (disjuncie exclusiv) i NOT (negaie). Folosim denumirile din englez
deoarece acestea reprezint i numele instruciunilor n limbaj de asamblare
echivalente. Fiecare operaie logic are un tabel de adevr. Repet, operaiile logice
se realizeaz bit cu bit, ntre biii de acelai rang.

AND
0
1

0
0
0

1
0
1

Rezultatul unei operaii AND este 1 numai atunci cnd


ambii bii sunt 1.

OR
0
1

0
0
1

1
1
1

Rezultatul unei operaii OR este 0 numai atunci cnd ambii


bii sunt 0.

XOR
0
1

0
0
1

1
1
0

NOT
0
1

Rezultatul unei operaii XOR este 0 atunci cnd ambii bii


sunt egali.
Funcia NOT schimb valoarea de adevr. Aplicat asupra
unui operand, rezult complementul fa de unu al
acestuia.

1
0

Exemple:
0000 1111 AND
1001 0110
0000 0110

1100 1001 OR
0100 1010
1100 1011

0011 0011 XOR


1111 0000
1100 0011

1100 1100 NOT


0011 0011
Cu excepia operatorului NOT, toi ceilali operatori logici necesit doi operanzi.
Ca de obicei, instruciunile logice primesc operanzi de 8, 16 sau 32 de bii. Sintaxa
general este de forma
OP destinaie,surs

care are ca efect


destinaie = destinaie OP surs
unde OP este operaia care se dorete a fi efectuat: AND, OR, XOR.
Destinaia poate fi un registru sau o locaie de memorie iar sursa un registru, o
locaie de memorie sau o constant de un anumit numr de bii.
n continuare prezentm operaiile tipice n care sunt utilizate instruciunile
logice.
tergerea rapid a unui registru

xor eax,eax
xor al,al

;eax = 0
;al = 0

XOR ntre operanzi identici duce la obinerea valorii 0.


1110 1100 XOR
1110 1100
0000 0000
Forarea unor bii n 0, restul rmnnd neschimbai
masca
equ
mov al, 0E3h
and al, masca

00001111
; al = 0000 0011

n urma operaiei I logic, biii din registru aflai pe poziia celor cu


valoarea 0 din masc, vor deveni 0, cei aflai pe poziia cu valoarea 1 din masc vor
rmne neschimbai.
Forarea unor bii n 1, restul rmnnd neschimbai
masca
equ
mov al,0E3h
or al,masca

00001111
; al = 1110 1111

n urma operaiei SAU logic, biii din registrul AL aflai pe poziia celor cu
valoarea 0 din masc vor rmne neschimbai, cei aflai pe poziie 1 din masc vor
fi setai.
Pe lng instruciunile echivalente funciilor logice, exist i instruciunea
TEST. TEST realizeaz operaia logic I bit cu bit ntre doi operanzi, dar nu
modific nici sursa nici destinaia. TEST nu genereaz rezultat, dar modific
indicatorii de stare. Este echivalentul logic al instruciunii CMP.
Instruciunea TEST ne permite s testm un singur bit dintr-un operand sau
s poziionm indicatorii de stare fr a modifica operandul. De exemplu, n cazul
instruciunii
test al,00000100b
indicatorul de zero se poziioneaz n 1 dac bitul din registru aflat pe poziia
echivalent a celui marcat cu 1 n masc este 0.

n urma instruciunii
test ax,ax
indicatorii de stare sunt poziionai ca i cnd a fost efectuat o instruciune AND,
dar coninutul registrului AX nu se modific.

7.7.2. Instruciuni de deplasare


Deplasrile pot fi logice sau aritmetice.
Instruciuni de deplasare logic
Acest grup de instruciuni realizeaz operaii de deplasare la nivel de bit,
spre stnga sau spre dreapta. Sunt folosite la nmulirea sau mprirea ntregilor
fr semn cu puteri ale lui doi.
De exemplu, 4 n binar este 0100. Deplasm un bit la stnga, 1000,
obinem 8. Dac reprezentm numrul 4 pe un octet: 0000 0100 i deplasm doi
bii la stnga, rezult: 00010 0000, adic 16. ntotdeauna, deplasrile logice cu un
bit nspre stnga trec bitul cel mai semnificativ n CF i completeaz bitul b! cu 0.
CF
0

CF
0

CF
0

Instruciunea care efectueaz operaii de deplasare logic spre stnga este


SHL (Shift logic Left). Are trei formate diferite:
shl destinaie
shl destinaie,CL
shl destinaie,imm8
unde destinaia poate fi registru sau locaie de memorie de 8, 16 sau 32 de
bii. Varianta cu un singur operand presupune c deplasarea se face cu un singur
bit (nmulim destinaia cu 2). Celelalte dou variante permit declararea numrului
de bii cu care va fi deplasat destinaia. Numrul poate fi specificat direct, ca
imediat de 8 bii (imm8), sau prin intermediul registrului CL.

Deplasarea logic spre dreapta mparte destinaia (ntreg fr semn) cu


puteri ale lui doi. Instruciunea este SHR (Shift Logic Right); cel mai puin
semnificativ bit va trece n CF, iar biii vacani se vor completa cu zero.
shr destinaie
shr destinaie,CL
shr destinaie,imm8
Operaiile de deplasare logic la stnga sau la dreapta sunt folosite la
nmulirea/mprirea ntregilor fr semn. n cazul unor operanzi cu semn nu dau
rezultatul scontat.
Unele limbaje de nivel nalt pun la dispoziia programatorului operatori
logici pe bii. De exemplu, limbajul C include urmtorii operatori logici: ~ (NOT),
& (AND), | (OR), ^ (XOR). Acetia sunt implementai cu instruciunile logice din
limbajul de asamblare. De asemenea, limbajul C dispune de operatori de deplasare
la stnga (<<), respectiv la dreapta (>>), echivalentul instruciunilor n limbaj de
asamblare SHL i SHR.
Instruciuni de deplasare aritmetic
Aceste instruciuni permit nmulirea sau mprirea rapid cu puteri ale lui
doi a ntregilor cu semn. Ele conserv bitul cel mai semnificativ (care, aa cum am
precizat la reprezentarea n complement fa de doi, poate fi gndit ca fiind bit de
semn).
sal destinaie,1
sar destinaie,1
sal destinaie,CL
sar destinaie,CL
sal destinaie,imm8
sar destinaie,imm8
Deoarece adugarea unor valori de zero n partea dreapt nu schimb bitul
cel mai semnificativ, SAL (Shift Arithmetic Left) este un alias pentru SHL (acelai
cod main). Toate numerele negative reprezentate n complement fa de doi au
MSB = 1. Ct timp deplasarea nu modifica bitul cel mai semnificativ rezultatul este
corect. Problema apare atunci cnd domeniul de reprezentare este depit. De
exemplu, pentru un octet, -128 deplasat la stnga cu 1 d 0.
Chiar dac este vorba de acelai cod main, YASM face diferena ntre
denumirile instruciunilor; n schimb, dezasamblorul NDISASM ntotdeauna
dezasambleaz acel cod ca fiind SHL.
;
;deplAritm.asm

;
section .text
global _start
_start:
nop
mov al,-64
shl al,1
movsx ebx,al
shl al,1
movsx ecx,al
mov al,-64
sal al,1
movsx ebx,al
sal al,1
movsx ecx,al
mov eax,1
mov ebx,0
int 080h
Instruciunea SAR (Shift Arithmetic Right) este diferit de instruciunea
SHR, deoarece completarea biilor vacani cu zero ar duce la suprascrierea celui
mai semnificativ bit (bitul de semn). Astfel, instruciunea SAR introduce bitul cel
mai puin semnificativ n CF i deplaseaz cu o poziie spre dreapta ceilali bii, dar
conserv bitul cel mai semnificativ.
CF
S 1 0 0 0 0 0 1
Astfel, dac deplasm un octet spre dreapta cu instruciunea SAR, numai
cei 7 bii mai puin semnificativi sunt deplasai. Cel mai semnificativ bit se
pstreaz.
;
;deplAritm2.asm
;
section .text
global _start
_start:
nop
mov al,-4
sar al,1

movsx ebx,al
mov al,-4
shr al,1
movsx ecx,al
mov eax,1
mov ebx,0
int 080h
Instruciuni de deplasare dubl
Setul de instruciuni cuprinde i dou instruciuni pentru deplasri de 32 i
64 de bii. Aceste dou instruciuni primesc ca operanzi de intrare cuvinte sau
dublu cuvinte i genereaz un rezultat de aceeai lungime (cuvnt sau dublu
cuvnt). Instruciunile de deplasare dubl au trei operanzi:
shld destinaie,surs,contor
shrd destinaie,surs,contor
unde destinaia i sursa pot fi cuvinte sau dublu cuvinte. Operandul
destinaie poate fi registru sau locaie de memorie, iar operandul surs obligatoriu
registru. Contorul numrului de bii deplasai poate fi specificat direct, ca valoarea
imediat, sau de registrul CL.
O diferen semnificativ fa de instruciunile de deplasare este c biii
introdui n destinaie prin deplasament provin de la operandul surs.
shld

shrd

15/31

CF

destinaie(reg/mem)

15/31

surs(reg)

15/31

15/31

surs(reg)

destinaie(reg/mem)

CF

Observai c biii deplasai n exteriorul operandului surs sunt introdui n


operandul destinaie. Totui, operandul surs nu se modific, el rmne la valoarea
avut anterior deplasrii. Numai operandul destinaie este actualizat. La fel ca n
cazul celorlalte instruciuni de deplasare, ultimul bit care prsete destinaia este
memorat n indicatorul de transport.

7.7.3. Instruciuni de rotaie


n cazul instruciunilor de deplasare, biii deplasai n afara operandului
destinaie sunt pierdui. Exist situaii n care este dezirabil s pstrm aceti bii.
Instruciunile de rotaie deplaseaz operandul spre stnga sau spre dreapta, exact ca
instruciunile de deplasare, numai c biii care depesc octetul, cuvntul sau dublu
cuvntul sunt reintrodui n octet, cuvnt sau dublu cuvnt prin cellalt capt al
valorii. De exemplu, rotaia unui octet la stnga cu un bit ia valoarea bitului 7 i o
plaseaz pe poziia bitului 0, iar ceilali bii sunt deplasai la stnga cu o poziie.
Instruciunile care realizeaz rotaii sunt: ROL (Rotate Left), ROR (Rotate
Right), RCL (Rotate Left and include CF), RCR (Rotate Right and include CF).
Ultimele dou instruciuni folosesc indicatorul CF ca bit adiional, lucru care
permite deplasarea a 9, 17 sau 33 de bii. Formatul instruciunilor de rotaie este
acelai cu formatul instruciunilor de deplasare. De exemplu:
rol destinaie,1
rol destinaie,CL
rol destinaie,imm8

7.7.4. Instruciuni de testare i modificare a unui bit


Setul de instruciuni conine patru instruciuni de testare i modificare a
unui bit specificat n instruciune prin deplasamentul su fa de bitul cel mai puin
semnificativ al operandului. Ca de obicei, bitul cel mai puin semnificativ este
considerat bitul de pe poziia 0. Toate instruciunile au acelai format. Dm ca
exemplu instruciunea:
bt operand,poziie
Operandul este un cuvnt sau dublu cuvnt aflat n memorie sau registru. Poziia
specific indexul bitului testat. Poate fi o valoare imediat sau un registru de 16 sau
32 de bii. Toate cele patru instruciuni copiaz bitul respectiv n CF i l modific
conform operaiei descrise de numele lor:
Instruciune
BT (Bit Test)
BTS (Bit Test and Set)
BTR (Bit Test and Reset)
BTC (Bit Test and Complement)

Efect asupra bitului selectat


niciun efect
bitul selectat este poziionat n 1
bitul selectat este poziionat n 0
bitul selectat este negat

Instruciunile din acest grup modific numai indicatorul de transport. Ceilali cinci

indicatori de stare rmn nemodificai.

7.7.5. Instruciuni de scanare pe bit


Instruciunile de scanare pe bit returneaz ntr-un registru poziia primului
bit de 1 ntlnit. Sunt dou instruciuni una pentru scanare direct (ncepe
scanarea de la bitul mai puin semnificativ), cealalt pentru scanare invers.
Formatul instruciunilor este:
bsf destinaie,operand
bsr destinaie,operand

;Bit Scan Forward


;Bit Scan Reverse

unde operandul este un cuvnt sau dublu cuvnt aflat n memorie sau registru.
Destinaia va reine indexul primului bit de 1 ntlnit n operand n timpul scanrii
directe sau inverse. Destinaia trebuie s fie obligatoriu un registru de 16 sau 32 de
bii. Dac toi biii sunt zero, indicatorul de zero se poziioneaz n 1; n caz
contrar, ZF = 0 i registrul destinaie memoreaz poziia. Instruciunile de scanare
pe bit afecteaz numai indicatorul de zero. Ceilali cinci indicatori de stare rmn
nemodificai.

7.7.6. Instruciuni de setare condiionat


Instruciunile din aceast categorie permit poziionarea unui octet pe zero
sau unu, n funcie de una din cele 16 condiii definite de indicatorii de stare.
Forma general a instruciunii este:
set<condiie> operand
unde operandul, de tip octet, poate fi registru sau locaie de memorie. Condiiile
sunt urmtoarele:
Instruciune
SETE/SETZ
SETNE/SETNZ
SETL/SETNGE
SETLE/SETNG
SETNL/SETGE
SETNLE/SETG
SETB/SETNAE/SETC

Condiie
ZF = 1
ZF = 0
SF < > OF, valori cu semn
SF < > OF sau ZF = 1, valori cu semn
SF = OF, valori cu semn
SF = OF i ZF = 0, valori cu semn
CF =1, valori fr semn

SETBE/SETNA
SETNB/SETAE/SETNC
SETNBE/SETA
SETO/SETNO
SETP/SETPE
SETNP/SETPO
SETS/SETNS

CF = 1 sau ZF = 1, fr semn
CF = 0, valori fr semn
CF = 0 i ZF = 0, fr semn
OF = 1 / respectiv OF = 0
PF = 1, paritate par
PF = 0, paritate impar
SF = 1 / respectiv SF = 0

Aceste instruciuni sunt folosite pentru implementarea expresiilor booleene din


limbajele de nivel nalt. Nu modific nici un indicator de stare.

7.8. Instruciuni de transfer al controlului


Rulai, prin intermediul depanatorului, oricare program din cele prezentate
pn acum, de exemplu deplAritm2.asm, i monitorizai la fiecare pas
valoarea registrului EIP. Observai cum execuia unei instruciuni modific
coninutul acestuia. Ei bine, coninutul registrului EIP reprezint adresa
instruciunii ce urmeaz a fi executat. EIP se numete contor de instruciune i
memoreaz adresa urmtoarei instruciuni din program. Pentru c instruciunile
sunt formate de obicei din mai muli octei, valoarea indicatorului de instruciune
crete n concordan cu numrul acestora i nu trebuie s ne ateptm s creasc
cu unu la fiecare instruciune executat.
Nici programatorul, nici programul nu poate modifica direct registrul EIP
(nici nu este de dorit). Altfel spus, nu putem folosi instruciunea MOV ca s
ncrcm n EIP adresa unei anumite locaii de memorie (de ex., adresa unei
anumite instruciuni). Pe de alt parte, exist instruciuni care modific indirect
valoarea EIP. Acestea sunt instruciunile de ramificare (salt) i iterative (buclele).
Asfel de instruciuni pot altera valoarea registrului EIP necondiionat sau
condiionat (modificarea se face numai dac este ndeplinit o anumit condiie
bazat pe starea indicatorilor din registrul EFLAGS). Pe baza acestui criteriu
putem mpri instruciunile de transfer al controlului programlui n dou categorii.
Instruciuni de ramificare necondiionat:
1. instruciuni de salt necondiionat
2. instruciuni de apel (call)
3. instruciuni de ntrerupere
Instruciuni de ramificare condiionat:
4. instruciuni de salt condiionat
n continuare vom studia numai instruciunile de salt necondiionat i

condiionat (1 si 4), urmnd ca instruciunile de apel i ntrerupere s fac obiectul


unor capitole viitoare.

7.8.1. Instruciuni de salt necondiionat


Formatul general al instruciunii de salt necondiionat este:
jmp

adres

unde adresa specific adresa de memorie de la care se va continua


rularea programului (adresa care va fi trecut n registrul EIP). Adresa de memorie
este declarat n cadrul codului surs ca etichet (asamblorul sau editorul de
legturi modific numele etichetei cu adresa corect).
;
;saltNcond.asm
;
section .data
val dd
105,106,107,100,110,103
section .text
global _start
_start:
nop
mov ebx,[val]
mov edi,1
mov eax,[val+edi*4]
cmova ebx,eax
jmp sfarsit
inc edi
mov ecx,[val+edi*4]
cmovnbe ebx,ecx
sfarsit:

mov eax,1
mov ebx,0
int 080h

Rulai programul pas cu pas i observai efectul instruciunii de salt


necondiionat asupra registrului EIP.
Dac dezasamblm programul cu comanda
objdump -D saltNcond -j .text -M intel

vedem adresa n clar:


08048080 <_start>:
8048080
90
8048081:
8b 1d b0 90 04 08
8048087:
bf 01 00 00 00
804808c:
8b 04 bd b0 90 04 08
8048093:
0f 47 d8
8048096:
eb 0b
8048098:
47
8048099:
8b 0c bd b0 90 04 08
80480a0:
0f 47 d9

nop
mov ebx,DWORD PTR ds:0x80490b0
mov edi,0x1
mov eax,DWORD [edi*4+0x80490b0]
cmova ebx,eax
jmp 80480a3 <sfarsit>
inc edi
mov ecx,DWORD [edi*4+0x80490b0]
cmova ebx,ecx

080480a3 <sfarsit>:
80480a3:
b8 01 00 00 00
80480a8:
bb 00 00 00 00
80480ad:
cd 80

mov eax,0x1
mov ebx,0x0
int 0x80

JMP indic procesorului c urmtoarea instruciune care trebuie executat se afl la


adresa specificat sub form de etichet. n momentul execuiei sale, valoarea
indicatorului de instruciune este schimbat cu adresa respectiv de memorie.

7.8.2. Instruciuni de salt condiionat


Instruciunile de salt condiionat transfer execuia programului la alt
adres numai dac este satisfcut o anumit condiie. Formatul general este:
j<condiie>

adres

unde <condiie> rezult din starea indicatorilor de stare CF, OF, PF, SF, ZF,
iar adresa este o etichet n cadrul codului.
Tabelul 7.8 Instruciuni de salt condiionat

Instruciune
Descriere
JA
Jump if Above
JAE
Jump if Above or Equal
JB
Jump if Below
JBE
Jump if Below or Equal
JC
Jump if Carry

Condiie
CF=0 and ZF=0
CF=0
CF=1
CF=1 or ZF=1
CF=1

JCXZ
JE
JG
JGE
JL
JLE
JMP
JNA
JNAE
JNB
JNBE
JNC
JNE
JNG
JNGE
JNL
JNLE
JNO
JNP
JNS
JNZ
JO
JP
JPE
JPO
JS
JZ

Jump if CX Zero
Jump if Equal
Jump if Greater (signed)
Jump if Greater or Equal (signed)
Jump if Less (signed)
Jump if Less or Equal (signed)
Unconditional Jump
Jump if Not Above
Jump if Not Above or Equal
Jump if Not Below
Jump if Not Below or Equal
Jump if Not Carry
Jump if Not Equal
Jump if Not Greater (signed)
Jump if Not Greater or Equal (signed)
Jump if Not Less (signed)
Jump if Not Less or Equal (signed)
Jump if Not Overflow (signed)
Jump if No Parity
Jump if Not Signed (signed)
Jump if Not Zero
Jump if Overflow (signed)
Jump if Parity
Jump if Parity Even
Jump if Parity Odd
Jump if Signed (signed)
Jump if Zero

CX=0
ZF=1
ZF=0 and SF=OF
SF=OF
SF != OF
ZF=1 or SF != OF
unconditional
CF=1 or ZF=1
CF=1
CF=0
CF=0 and ZF=0
CF=0
ZF=0
ZF=1 or SF != OF
SF != OF
SF=OF
ZF=0 and SF=OF
OF=0
PF=0
SF=0
ZF=0
OF=1
PF=1
PF=1
PF=0
SF=1
ZF=1

Rezultatul saltului condiionat depinde de starea registrului EFLAGS la


momentul execuiei instruciunii de salt. Fiecare instruciune de salt condiionat
testeaz condiia specific prin examinarea unor anumii indicatori de stare. La
prima vedere putem crede c unele instruciuni de salt condiionat sunt redundante.
De exemplu, JA i JG. Diferena const n faptul c unele lucreaz cu valori cu
semn, altele cu valori fr semn. Instruciunile de salt care folosesc cuvintele cheie
above i below sunt folosite pentru evaluarea valorilor ntregilor fr semn, n
timp ce greater i lower se refer la relaia dintre dou valori cu semn.
n funcie de distana la care se poate efectua saltul, sunt trei tipuri de
instruciuni de salt (condiionat sau nu):
SHORT saltul se poate efectua numai ntr-o raz de 128 de octei de
memorie (nainte sau napoi). Avantajul principal fa de urmtoarele

dou tipuri prezentate este c instruciunea de salt ocup mai puin


memorie. Pentru stocarea indexului de salt folosete un singur octet cu
semn. Indexul de salt este numrul de octei adunai sau sczui din
adresa aflat n registrul EIP la momentul respectiv. Un astfel de salt
este specificat prin cuvntul cheie SHORT introdus imediat dup
instruciunea de salt, nainte de etichet: jmp SHORT adres.
NEAR este tipul standard, att pentru salturile condiionate ct i
pentru cele necondiionate. Poate fi folosit la selectarea oricrei locaii
din segment. Arhitecturile de 32 de bii suport dou tipuri de salturi
NEAR. Unul memoreaz indexul de salt pe doi octei (permite saltul,
nainte sau napoi, ntr-o raz de aproximativ 32 000 de octei), cellalt
pe patru octei (lucru care permite adresarea oricrei locaii din
segmentul de cod, neles ca segment de 4 GB). n modul protejat, tipul
de salt implicit este NEAR cu patru octei de adres. Tipul NEAR cu doi
octei poate fi specificat prin cuvntul cheie WORD, astfel: jmp WORD
adres.
FAR este folosit n cazul modelului de memorie segmentat (Modul
REAL sau Virtual). Permite adresarea unei locaii aflate n alt segment
de cod. Instruciunile de salt condiionat nu suport acest tip de salt.

7.8.3. Instruciuni de ciclare


Scriem i rulm pas cu pas programul de mai jos.
;
;bucla.asm
;
section .text
global _start
_start:
nop
mov eax,5
mov ebx,2
mov edx,0
bucla:
inc edx
dec eax
cmp eax,ebx
jnz bucla
mov eax,1

mov ebx,0
int 080h
Atunci cnd valoarea din EAX devine 2, comparaia va seta indicatorul de
stare ZF i instruciunea de salt condiionat nu se va efectua. Astfel, ceea ce este
cuprins ntre eticheta bucla i instruciunea JNZ bucla este o secven de
program care se repet de un anumit numr de ori. Putem presupune c scopul este
s incrementm registrul EDX cu 3.
Dei putem folosi acest cod, Intel a pus la dispoziie un set de instruciuni
dedicate realizrii de bucle repetitive.
Tabelul 7.9 Instruciuni iterative

Instruciune
LOOP
LOOPE/LOOPZ
LOOPNE/LOOPNZ

Descriere
Repet bucla pn cnd registrul ECX devine 0
Repet bucla pn cnd registrul ECX devine 0 sau ZF = 0
Repet bucla pn cnd registrul ECX devine 0 sau ZF = 1

Sintaxa acestora este:


loop adres
unde adresa este o etichet din segmentul de cod la care se va efectua
saltul. Din pcate, instruciunile LOOP suport numai un deplasament de 8 bii, n
consecin pot fi realizate numai salturi de tip SHORT. Instruciunea LOOP
folosete n calitate de contor registrul ECX, cruia i decrementeaz automat
valoarea. Cnd acesta devine 0, LOOP nu se mai execut. nainte de nceputul
instruciunii LOOP trebuie s setm n ECX numrul de iteraii dorite.
<instruciuni nainte de bucl>
mov ecx,100
etichet:
<instruciuni n bucl>
loop etichet
<instruciuni dup bucl>
Observaii:
Dac codul din interiorul buclei altereaz valoarea registrului ECX,
aceast modificare este luat n consideraie.
Instruciunile LOOP nu schimb valoarea indicatorilor de stare. Cnd
ECX ajunge la zero, ZF nu este setat.
nainte s execute o instruciune LOOP, procesorul decrementeaz

valoarea din ECX cu 1, apoi verific daca aceasta este zero.


Programul poate fi rescris astfel:
;
;bucla.asm
;
section .text
global _start
_start:
nop
mov edx,0
mov ecx,3
bucla:
inc edx
loop bucla
mov eax,1
mov ebx,0
int 080h
Modificai valoarea de iniializare a registrului ECX cu 0. Explicai ce s-a
ntmplat. De cte ori s-a repetat bucla?

7.9. Procesarea irurilor


irul reprezint o secven de caractere. Exist cinci operaii de baz care
opereaz pe iruri, numite i primitive: mutare, comparare de iruri, scanarea unui
ir pentru o valoare, transfer la/de la acumulator. Pentru fiecare operaie exist o
instruciune dedicat.

7.9.1. Instruciuni de transfer


Presupunem c avem un ir n memorie i trebuie s copiem unul sau mai
multe elemente, poate chiar tot irul, n alt zon de memorie. Ne amintim c
instruciunea clasic de copiere, MOV, nu poate primi n acelai timp ca argumente
dou locaii de memorie, aadar nu poate copia date din memorie n memorie.
Pentru acest lucru, Intel a pus la dispoziie instruciunea MOVS. MOVS permite
programatorului s transfere pri sau iruri ntregi dintr-o locaie de memorie n
alta. Sintaxa general a instruciunii este:

MOVS<x>
unde x reprezint dimensiunea locaiei de memorie ce va fi tranferat la
execuia instruciunii: B, W sau D. Astfel, instruciunea mbrac trei forme:

MOVSB (MOVe String Byte) copiaz un singur octet.


MOVSW (MOVe String Word) copiaz un singur cuvnt.
MOVSD (MOVe String Double) copiaz un dublu cuvnt.

Chiar dac este considerat intruciune dedicat prelucrrilor pe iruri,


instruciunea MOVS, prin cele trei variante ale sale, copiaz unul, doi sau patru
octei o dat.
Fiecare din acestea are la rndul su dou variante, n funcie de numrul
de bii ai procesorului.
Pentru procesoarele de 16 bii, MOVSB copiaz un singur octet de la locaia
de memorie adresat de registrele DS:SI n locaia de memorie adresat de
registrele ES:DI (modul real de adresare a memoriei) i incrementeaz automat SI
i DI.
Pentru procesoarele de 32 de bii, MOVSB copiaz un singur octet de la
locaia de memorie adresat de registrele DS:ESI n locaia de memorie adresat de
registrele ES:EDI (modul protejat de adresare a memoriei) i incrementeaz
automat ESI (adresa operandului surs) i EDI (adresa operandului destinaie).
Instruciunile MOVSW i MOVSD au aceeai sintax, numai c vor copia
cte 2, respectiv 4 octei.
IA-32
Surs
ES:EDI

Destinaie
DS:ESI
7

Figura 7.4 Comportamentul instruciunii MOVSB

;sir.asm
;
section .data
sir1 db
10,20,30,40,50,60,70,80
section .bss
sir2 resb 8
section .text
global _start
_start:
nop
mov esi, sir1
;sau lea esi,[sir1]
mov edi, sir2
;sau lea edi,[sir2]
movsb
movsb
movsb
movsb
movsb
movsb
movsb
movsb

;sau putem folosi 4 instr. movsw sau 2 instr. movsd

mov eax,1
mov ebx,0
int 080h
Instruciunea LEA (Load Effective Address) calculeaz adresa efectiv al celui de
al doilea operand i o stocheaz n registrul specificat ca prim operand. n cazul de
mai sus, rezultatul este identic cu cel al instruciunii MOV ESI,sir1 (n acest caz
sir1 nu este inclus n paranteze ptrate deoarece ne intereseaz tocmai adresa).
Spre deosebire de instruciunea MOV, LEA necesit paranteze ptrate pentru cel de
al doilea operand al su:
lea edi,[sir1]

;EDI va conine adresa de nceput a sir1.

Instruciunea calculeaz adresa unui operand. n cazul exemplului


precedent, adresele irurilor s-au aflat uor prin instruciunea MOV, dar, aa cum am
specificat ntr-un capitol anterior, instruciunrea LEA este mai flexibil. LEA poate
determina adresa unui operand prin metode mai sofisticate:
lea

eax,[ebx+ecx*4+100]

Observaii:
Pentru a copia un ir de 8 octei, avem nevoie de 8 instruciuni MOVSB.
MOVSB nu face dect s copieze un singur octet de la adresa dat de
ESI la adresa dat de EDI i apoi s incrementeze automat ESI i EDI.
Putem micora numrul de linii ale programului nlocuind cele 8
instruciuni MOVSB cu 4 instruciuni MOVSW sau cu 2 instruciuni
MOVSD.
De asemenea, putem copia irul ncepnd cu primul ctre ultimul element
al irului (de la adres mai mic ctre adres mai mare) sau ncepnd de la ultimul
ctre primul element (de la adres mai mare ctre adres mai mic). Direcia de
execuie a instruciunii MOVS poate fi setat prin intermediul indicatorul de direcie
(DF - Direction Flag) din registrul EFLAGS.
dac DF = 0 (iniial), instruciunile MOVS incrementeaz ESI i EDI cu
1, 2 respectiv 4.
daca DF = 1, instruciunile MOVS decrementeaz ESI i EDI cu 1, 2,
respectiv 4.
Poziionarea indicatorului de direcie se face cu ajutorul instruciunilor
CLD (CLear Direction) i STD (SeT Direction). Cnd copiem irul de la sfrit la
nceput trebuie s fim ateni ca n ESI i EDI s avem adresele de sfrit. n acest
caz, exemplul trebuie modificat astfel:
lea
lea
std

esi, [sir1+7]
edi, [sir2+7]

Dac copiem un ir foarte mare, de exemplu un ir cu 1578 de octei,


trebuie s folosim foarte multe instruciuni MOVS. n acest caz este indicat s gsim
un mecanism de repetare automat a instruciunii MOVS. De exemplu, putem folosi
o bucl realizat cu ajutorul instruciunii LOOP:
mov ecx,8
bucla:
movsb
loop bucla
Codul rezultat funcioneaz. Prin folosirea instruciunii de buclare, MOVSB se
execut de opt ori i irul este copiat n ntregime. Totui, instruciunea LOOP nu
este foarte rapid (de fapt, se mai folosete din motive de compatibilitate, codul
DEC reg; JNZ este mai rapid), n plus, pentru astfel de cazuri, Intel a pus la

dispoziie o instruciune repetitiv dedicat, numit REP.


rep instruciune
Atenie, dup REP urmeaz ntotdeauna o instruciune.
Aceasta repet o instruciune de un numr de ori specificat n registrul
ECX. REP repet instruciunea care i urmeaz pn cnd ECX = 0. Schimbm n
program i rulm:
rep movsb
La rulare observm c instruciunea REP MOVSB a fost executat o
singur dat, nu de 8 ori precum LOOP. Un singur pas, dar dup acel pas, toi cei 8
octei ai irului surs sunt copiai la destinaie.
n fapt, REP nu este att o instruciune, ct un prefix al lui MOVSB.
Pe lng instruciunea REP, care monitorizeaz numai valoarea din
registrul ECX, mai sunt i alte variante care, pe lng ECX, dau atenie i la
indicatorul de stare ZF.
Tabelul 7.10 Variantele instruciunii REP

Instruciune
REPE
REPNE
REPZ
REPNZ

Descriere
Repeat while equal
Repeat while not equal
Repeat while zero
Repeat while not zero

Instruciunile REPE i REPZ denot aceeai instruciune (aliasuri). La fel i


REPNE cu REPNZ.
Pe lng faptul c putem copia iruri dintr-o locaie de memorie n alta,
avem la dispoziie i instruciuni capabile s transfere elementele irului ntre
memorie i acumulator.

7.9.2. Instruciuni de transfer la/de la acumulator


Instruciunea LODS (LOaD String) ncarc coninutul unei locaii de
memorie n acumulator. La fel ca instruciunea MOVS, are trei variante:
LODSB (Load String Byte) ncarc un octet din memorie n registrul
AL;

LODSW (Load String Word) ncarc un cuvnt din memorie n


registrul AX;
LODSD (Load String Double) ncarc un dublu cuvnt din memorie
n registrul EAX.

Fiecare din aceste forme are dou variante, n funcie de numrul de bii ai
procesorului.
Pentru procesoarele de 16 bii, LODSB copiaz un singur octet de la locaia
de memorie adresat de registrele DS:SI n registrul AL (modul real de adresare a
memoriei) i automat, n funcie de starea indicatorului DF, incrementeaz sau
decrementeaz SI.
Pentru procesoarele de 32 de bii, LODSB copiaz un singur octet de la
locaia de memorie adresat de registrele DS:ESI n registrul AL (modul protejat de
adresare a memoriei) i automat, n funcie de starea indicatorului DF,
incrementeaz sau decrementeaz ESI.
Instruciunile LODSW i LODSD au aceeai sintax, numai c vor copia
cte 2, respectiv 4 octei, n registrul AX, respectiv EAX, i vor incrementa sau
decrementa corespunztor ESI.

IA-32
AL
7
0

DS:ESI
7

Figura 7.5 Comportamentul instruciunii LODSB

Instruciunea STOS (STOre String) stocheaz ntr-o locaie de memorie


coninutul registrului acumulator. Are trei forme:
STOSB (Store String Byte) stocheaz n memorie octetul din
registrul AL.
STOSW (Store String Word) stocheaz n memorie cuvntul din
registrul AX.
STOSD (Store String Double) stocheaz n memorie dublu cuvntul
din registrul EAX.

ES:EDI
IA-32

AL
7
0
IA-32

Figura 7.6 Comportamentul instruciunii STOSB

Fiecare din aceste trei forme are dou variante, n funcie de numrul de
bii ai procesorului, similar instruciunilor MOVS i LODS.
Instruciunile STOSW i STOSD au aceeai sintax, numai c vor copia n
memorie cte 2, respectiv 4 octei, din registrul AX, respectiv EAX, i vor
incrementa sau decrementa EDI.
Urmtorul program exemplific comportamentul instruciunilor LODSD i
STOSD.
;
;addElem.asm
;
section .data
vector1 dd 10,20,30,40,50,60,70,80,90
section .bss
vector2 resd 9
section .text
global _start
_start:
nop
lea esi,[vector1]
lea edi,[vector2]
mov ecx,9
L1:
lodsd
add eax,5
stosd

dec ecx
jnz L1
mov eax,1
mov ebx,0
int 80h

7.9.3. Instruciuni de comparare


Instruciunea CMPS (CoMPare String) compar dou valori de tip octet,
cuvnt sau dublu cuvnt, ambele aflate n memorie. Seamn cu instruciunea
clasic de comparare a dou valori (CMP) o scdere fr rezultat, dar cu setarea
indicatorilor de stare. Aa cum ne-am obinuit, prezint trei forme:
CMPSB (Compare String Byte)
CMPSW (Compare String Word)
CMPSD (Compare String Double)
IA-32
Surs
ES:EDI

Destinaie
DS:ESI
7

Figura 7.7 Comportamentul instruciunii CMPSB

La fel ca la toate celelalte instruciuni de operare pe iruri, fiecare form de


COMPS are dou variante, n funcie de numrul de bii ai procesorului.
Instruciunile CMPSW i CMPSD au aceeai sintax, numai c vor
compara 2, respectiv 4 octei, i vor incrementa sau decrementa ESI i EDI.
Urmtorul program compar dou iruri de caractere. n cazul n care sunt
egale, valoarea din registrul EBX este 0. Dac nu sunt egale, valoarea din registrul
EBX indic numrul de caractere care mai erau de verificat.
;

;cmpStr.asm
;
section .data
sir1 db "Sir de caractere"
sir2 db "Sir decaractere"
section .text
global _start
_start:
nop
mov eax,1
lea esi,[sir1]
lea edi,[sir2]
mov ecx,16
cld
repe cmpsb
je egale
mov ebx,ecx
int 80h
egale:
mov ebx,0
int 80h
Nu este necesar s rulai programul prin intermediul depanatorului.
Valoarea din registrul EBX poate fi obinut din linie de comand cu:
./cmpStr
echo $?

7.9.4. Instruciuni de parcurgere


Instruciunea SCAS (SCAn String) permite scanarea irului pentru o
valoare dat. Cele trei forme sunt:
SCASB (Scan String Byte)
SCASW (Scan String Word)
SCASD (Scan String Double)
La fel ca la toate celelalte instruciuni de operaii pe iruri, fiecare form
are dou variante, n funcie de numrul de bii ai procesorului.
Instruciunile SCASW i SCASD au aceeai sintax, numai c vor compara
2, respectiv 4 octei, cu valorile din registrele AX, respectiv EAX, i vor incrementa

sau decrementa EDI.


Urmtorul program determin primul caracter egal cu un caracter dat
(caracterul spaiu) prin parcurgerea irului n sens direct.
;
;scanStr.asm
;
section .data
sir db "abcde jklmnoprst"
section .text
global _start
_start:
nop
lea edi,[sir]
mov ecx,20
cld
mov al,' '
repne scasb
je L1
mov ebx,0
mov eax,1
int 80h
L1:
mov eax,1
mov ebx,ecx
int 80

7.10. Operaii cu stiva


Stiva este o zon special a memoriei principale. Caracteristica stivei
const n modul n care sunt introduse i extrase datele. n mod obinuit, datele sunt
plasate n memoria principal secvenial, ncepnd cu locaia de la adresa mai mic
ctre locaia cu adresa mai mare. Stiva se comport exact invers. Memoria stiv
este rezervat la sfritul segmentului de memorie i, introducnd date n stiv,
vrful acesteia crete n jos. Adresa de nceput a stivei este memorat de registrul
indicator de stiv ESP. ESP conine adresa primei locaii libere din stiv. La
introducerea unui element n stiv, adresa scade, la extragerea unui element, adresa
crete. Primul element extras din stiv va fi ultimul intrat, ceea ce se numete LIFO
(Last In First Out).

7.10.1.

Introducerea i extragerea datelor

Datele sunt introduse n stiv cu instruciunea PUSH. Sintaxa acesteia este:


push surs
unde sursa poate fi registru de uz general sau locaie de memorie de 16
sau 32 de bii, sau imediat de 8, 16 sau 32 de bii. sursa poate fi i registru de
segment.
Extragerea datelor din stiv se face cu instruciunea POP.
pop destinaie
unde destinaia poate fi registru de uz general sau locaie de memorie
de 16 sau 32 de bii. destinaia poate fi i registru de segment.
;
;stiva1.asm
;
section .data
val dd 125
section .text
global _start
_start:
nop
mov ecx,244420
mov bx,350
mov eax,100
push ecx
push bx
push eax
;introduce n stiv adresa lui val
push val
;introduce n stiv valoarea lui val, valoare reprezentat pe patru octei
push dword [val]
pop
pop
pop
pop
pop

eax
eax
eax
ax
eax

mov eax,1

mov ebx,0
int 080h
Asamblm i rulm programul pas cu pas. Adresa de nceput a stivei se afl
n registrul ESP. La momentul acestei rulri adresa coninut de ESP este
0xbfc7b4f0. n urma introducerii registrului ECX n stiv, adresa devine
0xbfc7b4ec,

ceea ce nseamn o scdere cu 4. Putem afia valorile din stiv


prin comanda:
(gdb) x /4bx $esp
Reprezentm introducerea lui ECX n stiv:
ESP = 0xbfc7b4f0
0xbfc7b4ef
0xbfc7b4ee
0xbfc7b4ed
ESP = 0xbfc7b4ec

00
03
ba
c4
7

Executm urmtoarele dou instruciuni. Pn aici am introdus n stiv un


numr de 10 octei (doi de la BX i cte patru EAX, ECX). Stiva arat astfel:
ESP = 0xbfc7b4f0
0xbfc7b4ef
0xbfc7b4ee
0xbfc7b4ed
ESP = 0xbfc7b4ec

00
03
ba
c4
01
5e
00
00
00
64
7

ESP = 0xbfc7b4ea

ESP = 0xbfc7b4e6
0

Instruciunile PUSH VAR i PUSH DWORD [VAR] exemplific faptul c

putem stoca n stiv att o valoare ct i adresa unei valori.


Continuai rularea i observai cum se modific adresa din registrul ESP
pentru fiecare instruciune POP.

7.10.2.

Introducerea i extragerea registrelor

Urmtoarele perechi de instruciuni salveaz sau extrag din stiv starea


curent a tuturor registrelor de uz general.
Tabelul 7.11 Introducerea sau extragerea registrelor n stiv

Instruciuni
Descriere
PUSHA/POPA Introduce/extrage din stiv toi regitrii de uz general de 16 bii
PUSHAD/POPAD Introduce/extrage din stiv toi regitrii de uz general de 32 de
bii
PUSHF/POPF Introduce/extrage din stiv primii 16 bii mai puin
semnificativi din reg. EFLAGS
PUSHFD/POPFD Introduce/extrage din stiv registrul EFLAGS
Registrele salvate n stiv de ctre instruciunea PUSHAD sunt: EAX, ECX,
EDX, EBX, ESP, EBP, ESI, EDI. Primul registru care poate fi extras este EDI.
Instruciunea POPAD extrage din stiv n ordinea invers a salvrii lor cu PUSHAD.
;
;stiva2.asm
;
section .bss
val resd 1
section .text
global _start
_start:
nop
mov eax,0ffffaaaah
mov ebx,012345678h
mov ecx,055556666h
mov edx,011112222h
mov ebp,0bbbbcccch
mov esi,0ddddeeeeh
mov edi,088889999h
pusha
pushf
popf

popa
pushad
pop dword [val]
pushfd
popad
popfd
mov eax,1
mov ebx,0
int 080h

7.10.3.

Exemple de lucru cu stiva

Instruciunile PUSH i POP nu sunt singurele care pot introduce sau extrage
date din stiv. Putem introduce sau extrage date din stiv prin adresare bazat,
folosind ca adres de baz adresa memorat n registrul ESP. De cele mai multe ori
ns, n locul utilizrii directe a lui ESP, se copiaz adresa din ESP n EBP.
Instruciunile care acceseaz parametrii stocai n stiv folosesc ca adres de baz
valoarea lui EBP. Dar nu intrm acum n detalii, acesta este subiectul unui capitol
viitor.
Stiva este utilizat n trei scopuri principale:
spaiu de stocare a datelor temporare;
transferul controlului n program;
transmiterea parametrilor n timpul unui apel de procedur.
n paragrafele urmtoare dezbatem primul caz, cel al salvrii temporare a datelor,
celelalte dou sunt studiate pe larg n capitolul intitulat Funcii.
De exemplu, s presupunem c dorim s interschimbm coninutul a dou
locaii de memorie de 32 de bii. Nu putem interschimba coninutul acestora direct,
deoarece instruciunea XCHG nu poate primi ca operanzi dou locaii de memorie.
ns putem folosi un registru intermediar. Secvena de instruciuni
mov eax,[val1]
xchg [val2],eax
mov [val1],eax
ndeplinete sarcina propus, dar are nevoie de un registru suplimentar. Din cauza
numrului limitat de registre de uz general, acesta nu este tocmai uor de gsit. De
obicei conine date care trebuie salvate nainte de secven i refcute dup, ca n
urmtorul exemplu:
;

;swap.asm
;
section .data
val1 dd
125
val2 dd
225
section .text
global _start
_start:
nop
mov eax,0ffffffffh
push eax
mov eax,[val1]
xchg eax,[val2]
mov [val1],eax
pop eax

;salvm coninutul registrului EAX

;aducem registrul EAX la starea iniial

mov eax,1
mov ebx,0
int 080h
Codul acceseaz memoria de cinci ori. V amintii c instruciunea XCHG,
n cazul n care acceseaz memoria, este mare consumatoare de timp? Din
considerente de performan, probabil ar fi fost mai indicat s folosim patru
instruciuni MOV, astfel:
mov
mov
mov
mov

eax,[val1]
ebx,[val2]
[val1],ebx
[val2],eax

n acest caz, programul complet acceseaz memoria de opt ori (4 instruciuni MOV
plus 4 instruciuni necesare salvrii i restaurrii registrelor intermediare). Dar stiva
este o structur de tip LIFO, secvena de instruciuni POP realizeaz procesul
invers al secvenei de instruciuni PUSH. O modalitate elegant de interschimbare a
unor valori este s folosim numai stiva.
;
;swapWithStack.asm
;
section .data
var1 dd
125
var2 dd
225
section .text

global _start
_start:
nop
mov eax,0ffffffffh
push [var1]
push [var2]
pop [var1]
pop [var2]
mov eax,1
mov ebx,0
int 080h
De aceast dat nu este necesar salvarea coninutului vreunui registru (nu mai
folosim registre intermediare). Observai i faptul c instruciunile PUSH i POP
permit transferul datelor de la memorie la memorie.
Stiva este folosit frecvent atunci cnd trebuie eliberat un set de registre
utilizat de secvena de cod care urmeaz.

7.11. Exerciii

8. OPERAII CU NUMERE N VIRGUL


MOBIL

n capitolele anterioare am lucrat numai cu numere ntregi. Aa cum tii,


unele relaii numerice nu pot fi definite prin intermediul numerelor ntregi i a fost
introdus conceptul de fracie. Astfel, ntre dou numere ntregi putem avea un
numr infinit de valori. Pe lng numrul infinit de valori ntre dou numere
ntregi, n sistemul de numeraie exist un numr infinit de numere ntregi. Toate
aceste numere combinate formeaz domeniul numerelor reale. Numerele reale pot
conine orice valoare numeric de la minus infinit la plus infinit, cu orice numr de
cifre zecimale dup virgul. Acest capitol descrie standardele de reprezentare a
numerelor reale, arhitectura unitii de calcul n virgul mobil pentru
arhitecturile Intel de 32 de bii i operaiile principale pe care procesoarele de
acest tip le pot efectua asupra numerelor reale.

8.1. Reprezentarea numerelor reale


Reprezentarea numerelor reale ntr-un sistem de calcul este o provocare,
mai ales c acestea au dimensiuni diferite (magnitudine). Primele procesoare Intel,
de la 8086 pn la 80386 inclusiv, nu puteau opera cu numere reale. Pentru
reprezentarea acestora Intel punea la dispoziie coprocesoarele aritmetice separate
(8057, 80287 i 80387). De abia ncepnd cu 486 operaiile n virgul mobil sunt
realizate de o unitate integrat, denumit prescurtat FPU (Floating Point Unit).

8.1.1. Formatul n virgul mobil


Formatul n virgul mobil a fost dezvoltat ca metod standard pentru
reprezentarea numerelor reale n sistemele de calcul. Formatul n virgul mobil
reprezint numerele reale utiliznd notaia tiinific (exponenial).
n notaia tiinific zecimal numerele sunt exprimate prin dou pri: o
parte fracional cu semn, numit mantis, i o parte exponenial ce indic puterea
lui 10 la care trebuie ridicat mantisa astfel nct s obinem valoarea numrului.
Aadar, pentru a exprima 0.125 n notaie tiinific, putem scrie 1.2510!! .
Sistemele de calcul folosesc numere binare. Deoarece numerele sunt n

format binar, mantisa i exponentul trebuie reprezentate ca valori binare, nu


zecimale. Exemplul nostru n formatul exponenial binar devine 1.02!! .
Ne amintim conversia din zecimal n binar, este un proces n doi pai:
convertim partea ntreag n binar prin mprire succesiv la 2;
convertim partea fracionar multiplicnd succesiv cu 2 pn cnd
ajungem la zero, i reinnd partea ntreag.
Convertim 23.25 din zecimal n binar:
convertim 23
23: 2 = 11 rest
11: 2 = 5 rest
5: 2 = 2 rest
2: 2 = 1 rest
1: 2 = 0 rest
convertim .25
.25 2 = 0.50
.50 2 = 1.0
Rezultat final: 10111.01
Convertim 0.85:
convertim .85
.85
.7
.4
.8
.6
.2
.4

2
2
2
2
2
2
2

=
=
=
=
=
=
=

1
1
1
0
1

1.7
1.4
0.8
1.6
1.2
0.4
0.8

Nu ajungem niciodat la zero. Acest numr are o reprezentare finit n zecimal, dar
infinit n binar. Echivalentul lui 1/6 n zecimal (0.1666666...). Calculatorul va
putea numai s-l aproximeze. O caracteristic cheie a operaiilor cu numere ntregi
este c rezultatul este ntotdeauna precis. De exemplu, dac adunm doi ntregi,
obinem ntodeauna rezultatul exact. n contrast, operaiile cu numere reale sunt
ntotdeauna predispuse la aproximri.
Numerele reale reprezentate n notaie tiinific au trei componente
N = (1)! M2!
unde,

S reprezint bitul de semn


M, mantisa

E, exponent

Sistemele digitale reprezint aceste componente sub forma unor cmpuri


de dimensiune fix:
S

Cmpul S are ntotdeauna un singur bit i reprezint semnul mantisei. n schimb,


numrul de bii utilizai pentru exponent i mantis depinde de optimizrile dorite.
Mrimea cmpului E determin dimensiunea domeniului de reprezentare, iar
mrimea cmpului M precizia reprezentrii.
Un format oarecare
n aceast seciune presupunem un format de reprezentare n virgul mobil de 14
bii, cu un exponent de 5 bii, o mantis de 8 bii i un bit de semn.
1bit
5 bii
Semn Exponent

8 bii
Mantis

Mantisa numrului n virgul mobil conine valoarea fracionar n binar i este


ntotdeauna precedat de un punct binar implicit. Exponentul reprezint puterea lui
2 la care este ridicat mantisa.
Reprezentm numrul 32. tim c 32 este 2! , aadar, n notaia tiinific
binar 32 = 1.02! = 0.12! .!Folosind aceast informaie, introducem 110 (= 6
n zecimal) n cmpul exponent i 1 n mantis, astfel:
0 0 0 1 1 0 1 0 0 0 0 0 0 0

O problem evident a acestui model este c nu poate reprezenta exponeni


negativi. Nu avem nicio posibilitate s reprezentm 0.125 de exemplu, deoarece
0.125 este 2!! i exponentul -3 nu poate fi reprezentat. Am putea rezolva problema
prin adugarea unui bit de semn pentru exponent, dar s-a dovedit c este mult mai
eficient s deplasm (adunm la) valoarea iniial a acestuia cu o valoare
predefinit (bias). Ideea din spatele valorii predefinite este de a transforma fiecare
valoare exponent ntr-un ntreg pozitiv. Valoarea predefinit este un numr aflat la
mijlocul intervalului exponent i reprezint zero. n cazul nostru, valoarea
predefinit este 16 deoarece este la mijlocul intervalului 0 i 31 (exponentul nostru
are 5 bii, aadar permite 32 de valori). Orice numr mai mare ca 16 n cmpul
exponent indic o valoare pozitiv. Numerele mai mici de 16 indic valori
negative. Acest mod de reprezentare se numete reprezentare n exces cu 16
(excess-16) deoarece pentru a obine valoarea real a exponentului trebuie s

scdem 16. Reinei c exponenii care au toi biii de zero sau unu sunt rezervai
pentru numere speciale (precum zero sau infinit).
Revenind la reprezentarea numrului 32, valoarea iniial a exponentului, 6, se
adun cu valoarea predefinit, 16, i rezult 22:
0 1 0 1 1 0 1 0 0 0 0 0 0 0

Dac am dori s reprezentm numrul 0.125 = 1.02!! = 0.12!! , am avea 16


+ (-2) = 14.
0 0 1 1 1 0 1 0 0 0 0 0 0 0

Dar mai ntmpinm o problem deloc de neglijat. Numerele pot avea mai
multe reprezentri. Altfel spus, sistemul nu garanteaz unicitatea reprezentrii.
Toate formele urmtoare reprezint numrul 32 (0.12! , 0.012! , 0.0012! ,
etc.).
0 1 0 1 1 0 1 0 0 0 0 0 0 0
0 1 0 1 1 1 0 1 0 0 0 0 0 0
0 1 1 0 0 0 0 0 1 0 0 0 0 0
0 1 1 0 0 1 0 0 0 1 0 0 0 0

Nu numai c aceste sinonime ocup spaiu suplimentar, dar pot provoca


confuzii. n consecin, s-a stabilit ca virgula s fie plasat dup primul digit diferit
de 0. Acesta se numete normalizare. n acest mod, mantisa stocheaz numai
partea fracional a numrul normalizat.
Astfel, dac 32 este n binar 100000, normalizat nseamn c mutm
virgula n dreapta primei cifre de 1. Valoarea noastr binar devine 1.000002!
sau 1.02! . n consecin, aceasta este valoarea care va fi reprezentat. Bitul din
stnga punctului binar nu se mai reprezint, fiind ntotdeauna 1, i convenia ofer
efectiv un bit suplimentar de precizie (crete precizia cu o putere a lui 2). Totui,
reprezentarea 0.0 are nevoie de atenie special.
Reprezentarea lui 32 este:
0 1 0 1 0 1 0 0 0 0 0 0 0 0

Formatul acestui numr normalizat poate fi reprezentat astfel:


N = (1)! (1. M)2!!!"#$

unde, ca s aflm exponentul, scdem valoarea prestabilit din cmpul E.


Operaii aritmetice cu numere n virgul mobil
Adunarea i scderea n virgul mobil se face n mod obinuit. Primul
lucru de care trebuie s inem cont este s exprimm ambii operanzi n aceeai
putere exponenial, apoi se adun numerele din mantis pstrnd exponentul. n
caz c este nevoie, exponentul se ajusteaz la final. n urmtorul exemplu calculm
suma numerelor 12 i 1.25 folosind modelul de reprezentare pe 14 bii.
12 = 1.12! , iar 1.25 = 0.1012! = 0.001012!
0 1 0 0 1 1 1 0 0 0 0 0 0 0 +
0 1 0 0 1 1 0 0 1 0 1 0 0 0

0 1 0 0 1 1 1 0 1 0 1 0 0 0

Aadar, suma este 1.101012! = 1101.01 = 13.25. Observai c pentru 12 am


folosit forma normalizat. 1.25 a trebuie adus la forma exponentului lui 12.
nmulirea este la fel de simpl, se nmulesc valorile din mantis i se
adun exponenii. Dac exponentul are nevoie, se ajusteaz la final. nmulim 12 cu
1.25. Nu folosim formele normalizate: 0.112! i 0.1012! . n plus, de aceast
dat nu este nevoie s aducem exponenii la aceeai valoare.
0 1 0 1 0 0 1 1 0 0 0 0 0 0
0 1 0 0 0 1 1 0 1 0 0 0 0 0

0 1 0 1 0 1 0 1 1 1 1 0 0 0

nmulim mantisa 0.1100.101 = 0.01111i adunm exponenii 2! + 2! = 2! .


Obinem 0.011112! = 1111.0 = 15.
Definiii i erori
Cnd discutm de numerele n virgul mobil este important s nelegem termeni
ca domeniu de reprezentare, precizie, exactitate.
Domeniul unui format numeric ntreg reprezint diferena ntre valoarea
cea mai mare i cea mai mic care poate fi reprezentat. Acurateea se refer la ct

de exact aproximeaz o reprezentare numeric valoarea real (ct de aproape este


reprezentarea de valoarea corect). Precizia unui numr indic cantitatea de
informaie de care dispunem pentru reprezentarea unei valori. Precizia se refer la
numrul de bii pe care i are mantisa la dispoziie ca s reprezinte un numr. Un
numr poate fi precis, dar nu exact. De exemplu, spunem c nlimea cuiva este
1.7402 m, lucru foarte precis (precizie de aproximativ 1/1000). Totui, poate fi
inexact, deoarece nlimea persoanei poate fi cu totul alta. n tiin, precizia este
definit de numrul de bii ai mantisei. De exemplu, 2.3410!" are aceeai
precizie cu 2.3410!!" , dei a dou valoare este mult, mult mai mic dect prima.
Acesta este o precizie diferit de cea cu care probabil suntem obinuii. De
exemplu, dac avem un cntar cu precizie de 1kg, eroarea poate fi de +/- 1/2 kg.
Majoritatea consider precizia ca fiind valoarea minim care poate fi msurat.
Indiferent de ci bii avem la dispoziie pentru a reprezenta un numr real,
modelul nostru trebuie s fie finit. Cnd folosim sistemele de calcul pentru operaii
n virgul mobil, nu facem dect s modelm un sistem infinit de numere reale
ntr-un sistem finit de numere ntregi. Ceea ce obinem n mod real, este o
aproximare a sistemului numerelor reale. Cu ct folosim mai muli bii, cu att
aproximaia este mai exact. Totui, la un moment dat, orice model de reprezentare
i atinge limitele i calculele sunt afectate de erori. Prin creterea numrului de bii
vom putea reduce numrul acestora, dar nu vom putea niciodat s le eliminm.
Rolul nostru este s reducem posibilitile de apariie a erorilor sau cel puin s fim
contieni de magnitudinea acestora n calculele noastre. Erorile pot crete prin
operaii aritmetice repetitive. De exemplu, este evident c modelul nostru de 14 bii
nu poate reprezenta valori ce depesc capacitatea de 8 bii a mantisei, dar mai
puin evident este faptul c nu poate reprezenta exact nici valori din interiorul
domeniului de reprezentare. De exemplu, 128.5, convetit n binar rezult
10000000.1, 9 bii. Cum mantisa poate reprezenta 8 bii, n mod obinuit, bitul cel
mai mai puin semnificativ este eliminat i rotunjit la urmtorul bit. Deja am
introdus o eroare n sistemul nostru.
Erorile pot fi evidente, subtile sau neobservate. Erorile evidente, precum
depirea valorii maxime sau minime a domeniului duc la ntreruperea
programului. Erorile subtile pot genera rezultate eronate greu de detectat pn nu
ncep s produc probleme.

8.1.2. Standardul IEEE 754


Modelul de reprezentare al numerelor n virgul mobil din seciunea
anterioar are rol educativ, dar putem extinde acest model cu orice numr dorit de
bii. Pn n anii '80 deciziile de acest tip erau des ntlnite, fiecare productor de
sisteme avea propriul format. Acestea foloseau un numr diferit de bii pentru
reprezentarea mantisei i exponentului, diferite modaliti de tratare a erorilor

cauzate de depirea domeniului de reprezentare sau a condiiilor speciale


(mprirea la 0, extragerea rdcinii ptrate dintr-un numr negativ, etc.). n 1985,
IEEE a publicat un standard de reprezentare a numerelor n virgul mobil n
simpl i dubl precizie. Acest standard este cunoscut oficial ca IEEE-754 (1985).
Reprezentarea n simpl precizie
Standardul IEEE-754 n simpl precizie utilizeaz un exponent de 8 bii i
o valoare predefinit (bias) egal cu 127 (excess-127). Mantisa are 23 de bii i este
normalizat cu un bit ascums 1.M. mpreun cu bitul de semn, dimensiunea
total a formatului este de 32 de bii.
N = (1)! 2!!!"# (1. M)
Reinem c 1 < E < 254. Valorile 0 i 255 reprezentnd cazuri speciale.
Domeniul de reprezentare valid pentru formatul n simpl precizie poate fi
definit ca fiind:
2!"#$%& !"#$"%& (2 2!!!"#"$%&'%(")"% )2!"#$#% , pentru valori pozitive
2!"#$%& !"#$"%& (2 2!!!"#"$%&'%(")"% )2!"#$#% , pentru valori negative
unde,
minexp este valoarea legal minim a exponentului pentru numere
normalizate. Aceasta este egal cu -126 care, atunci cnd se adun
valoarea predefinit de 127, va rezulta un exponent deplasat de valoare
1 (00000001b).
maxexp este valoarea legal maxim a exponentului pentru numere
normalizate. Aceasta este 127 care, adunat cu valoarea predefinit
127, va rezulta un exponent deplasat de valoare 254 (11111110b).
bitideprecizie este numrul biilor din mantis. Pentru precizia simpl,
acest valoare este 24 (23 plus bitul implicit).
Introducnd valorile enumerate n expresii, rezult urmtoarele domenii de valori:
2!!"# !"#$"%& (2 2!!" )2!"# , pentru numere pozitive
2!!"# !"#$"%& (2 2!!" )2!"# , pentru numere negative
ceea ce nseamn, cu aproximaie, de la 1.1810!!" pn la 3.4010!" .
Numrul 0 este un caz special a lui E = 0 (irul binar 00000000) i are dou
reprezentri. La fel, cnd E = 255 (irul binar 11111111), valoarea reprezentat
este (mantisa este egal cu zero) sau NaN (mantisa este diferit de zero).
Valoarea NaN (Not a Number) reprezint un numr invalid folosit de obicei ca
indicator de eroare. Valorile speciale sunt redate n tabelul urmtor:

Tabelul 8.1 Valorile speciale ale reprezentrii n simpl precizie

Condiie
E = 255 i M 0
E= 255 i M = 0
E = 0 i M 0
E= 0 i M = 0

Valoare N
NaN
(1)!
(1)! 2!!"# (0. M)
(1)! 0

Reprezentri
Not a Number

Numr denormalizat
0

Zero
Zero nu poate fi reprezentat direct. Deoarece avem bit de semn, pentru 0 exist
reprezentare pozitiv i negativ.
Infinit
Avem reprezentare separat pentru plus i minus infinit. Un numr diferit de zero
mprit la zero d infinit. De exemplu, 1.0/0.0 produce infinit.
NaN
De obicei apare n cazurile unor operaii definite greit. Exemplul standard este
mprirea 0.0/0.0, care nu are o valoare definit. Ele sunt identificate printr-un
exponent care are toi biii de 1 i o mantis diferit de cea pentru configuraia
valorilor de tip infinit. Exist dou tipuri de astfel de configuraii, denumite NaN
de semnalizare (SNaN Signaling NaN) i NaN tcut (QNaN Quiet NaN).
SNaN are mantisa de forma 1.0xx ... x, unde x poate avea orice
valoare (dar nu pot fi toi zero, deoarece reprezint valoarea infinit). Cnd
ntlnete o astfel de configuraie FPU genereaz o excepie ce semnalizeaz
efectuarea unei operaii invalide. Programatorul o poate folosi pentru a indica unele
condiii de eroare, cum ar fi o variabil n virgul mobil neiniializat.
Cealalt configuraie, QNaN, are mantisa de forma 1.1xxx...x. Primul
1 este cel implicit. O astfel de configuraie apare cnd rezultatul unei operaii
aritmetice este nedefinit matematic.
Orice instruciune care are un operand de unul din cele dou tipuri de NaN
va genera un rezultat de tip NaN.

Numere denormalizate
Fa de numerele normalizate, acestea sunt numere cu mai puini bii de precizie i
valori mai mici. S presupunem c permitem tuturor biilor dintr-un numr

normalizat s fie 0 (n fapt este chiar reprezentarea valid pentru 0). Un ir de 32 de


bii de zero ar fi 1.02!!"# . O valoare mic, dar am putea reprezenta numere mult
mai mici dac pentru un E = 0000 0000b:
renunm la bitul de 1 ascuns
i fixm valoarea exponentului la -126.
V amintii c exponentul este scris ca valoare adunat cu 127? n
consecin, v-ai atepta ca din moment ce biii cmpului E sunt 0000000, acetia
trebuie s reprezinte un exponent de 127, nu -126. Totui, e un motiv ntemeiat
pentru care este -126. Vom explica puin mai trziu. Deocamdat acceptm faptul
c valoarea exponentului este -126 ori de cte ori irul de bii din cmpul exponent
este 0! (8 bii de zero).
Aadar, cel mai mare numr pozitiv denormalizat este format din irul de
bii (semn, exponent, mantis):
0

0000 0000

1111 1111 1111 1111 1111 111

care este mapat la numrul 0. (1!" )2!!"# , unde 1!" nseamn un ir de 23 de bii
de 1. Din moment ce punctul binar este urmat de 23 de bii, acest numr are
precizie de 23 de bii.
Cel mai mic numr denormalizat este format din irul de bii:
0

0000 0000

0000 0000 0000 0000 0000 001

care se traduce prin 0.0!! 12!!"# , sau 1.02!!"# . Acest numr are un singur bit
de precizie. Cei 22 de zero nu afecteaz precizia numrului. Dac nu credei,
gndii-v c numrul zecimal 256 are trei digii de precizie, iar 00256 are tot trei
digii de precizie. Cifrele de zero nu afecteaz numrul digiilor de precizie. n mod
similar, dac avem 0.000256, cifrele de zero ne ajut s plasm 256 corect, dar nu
sunt digii care trebuie plasai n mantis. 0.02560 are patru digii de precizie
deoarece cifra 0 din dreapta se adaug preciziei. Aadar, n urma punctului binar
avem 22 de bii de zero urmai de un 1, i cei 22 de zero nu au legtur cu numrul
de bii ai mantisei.
Prin denormalizare am obinut 1.02!!"# ca fiind cel mai mic numr, spre
deosebire de 1.02!!"# , cea mai mic valoare pe care am fi obinut-o dac
numrul ar fi fost normalizat. S-au sacrificat totui 22 de bii de precizie.
Un ir de bii 0! n cmpul exponent este mapat la un exponent -126.
Totui, pentru numerele n virgul mobil reprezentate cu standardul IEEE-754,
valoarea predefinit este -127. De ce este -126 n loc de -127?
Ca s rspundem la aceast ntrebare trebuie s ne uitm la cel mai mic
numr pozitiv normalizat,

0000 0001

0000 0000 0000 0000 0000 000

adic 1.02!!"# .
S studiem cele dou posibiliti pe care le avem pentru a reprezenta cel
mai mare numr pozitiv denormalizat.
0. (1!" )2!!"# (bias 127)
0. (1!" )2!!"# (bias 126 - acesta este folosit de IEEE 754 n simpl
precizie)
Amndou sunt mai mici dect valoarea minim a numerelor normalizate,
1.02!!"# . Acesta este un lucru bun, deoarece dorim s evitm suprapunerea
numerelor normalizate i denormalizate. De asemenea, observai c numrul cu
exponent -126 este mai mare dect numrul cu exponent -127. Aadar, prin
alegerea lui -126, diferena dintre cel mai mic numr normalizat i cel mai mare
numr denormalizat este mai mic. Aceasta este i raiunea pentru care standardul
n simpl precizie IEEE-754 folosete ca cel mai mic numr denormalizat
0. (1!" )2!!"# .
Reprezentarea n dubl precizie
Numerele n dubl precizie folosesc un cuvnt cu semn format dintr-un
exponent de 11 bii i o mantis de 52 de bii, pentru un total de 64 de bii.
Valoarea predefinit este 1023 (excess-1023).
Valorile pe care le poate lua acest format sunt prezentate n Tabelul 8.2.
Tabelul 8.2 Valorile reprezentrii n dubl precizie

Condiie
E = 2047 i M 0
E= 2047 i M = 0
0 < E < 2047
E = 0 i M 0
E= 0 i M = 0

Valoare N
NaN
(1)!
(1)! 2!!!"#$ (1. M)
(1)! 2!!"## (0. M)
(1)! 0

Reprezentri
Not a Number

Numr normalizat
Numr denormalizat
0

Aceast combinaie produce un domeniu de valori ntre 2.2310!!"# i


1.7910!"# .

8.1.3. Valori n virgul mobil specifice IA-32


Platformele IA-32 folosesc formatele standardului IEEE 754, n simpl i
dubl precizie, mpreun cu propriul format de 80 de bii, numit formatul
numerelor n precizie extins, destinat uzului intern. n C, folosim directiva
float, pentru numere n simpl precizie i double, pentru numere n dubl

precizie, dar, aa cum vom vedea n seciunile urmtoare, toate registrele interne
ale unitii de calcul n virgul mobil sunt de 80 de bii.
Formatul numerelor n virgul mobil cu precizie extins folosete 64 de
bii pentru mantis i 15 bii pentru exponent. Valoarea predefinit este 16.383,
genernd exponeni ntre -16382 i +16383, pentru un domeniu de valori cuprins
ntre 3.3710!!"#$ i 1.1810!"#$ .

8.2. Arhitectura unitii n virgul mobil


Unitatea n virgul mobil conine registrele din Figura 8.1. Acestea sunt
mprite n trei grupuri:
registre de date,
registre de control i stare,
registre indicator.
Ultimul grup conine registrul indicator de instruciune i registrul indicator
de date i ofer informaii necesare n scrierea rutinelor de ntrerupere care trateaz
erorile aprute n timpul operaiilor. Din moment ce acest subiect depete
obiectivele acestei cri, nu intrm n detalii.
79

64 63

0
1
2
3
4
5
6
7

15

0
Control

15

0
Stare

Exponent

Mantis

Tag

Figura 8.1 Registrele unitii n virgul mobil

8.2.1. Registre de date


Unitatea n virgul mobil dispune de 8 registre de date, de cte 80 de bii,
organizate ca o stiv de registre. Dei pot fi adresate individual pe baza numelui,
deoarece sunt organizate ca stiv de registre, aceste nume nu sunt atribuite static.
Adresarea registrelor se face relativ la registrul din vrful stivei. ST0 nu se refer la

un registru specific, ci la registrul aflat n acel moment n vrful stivei (TOS Top
Of Stack). Registrul urmtor este numit ST1 .a.m.d., pn la ultimul registru,
ST7. Vrful curent al stivei este indicat de un cmp de trei bii aflat n registrul de
stare, TOP (stack TOP). Operaiile de ncrcare decrementeaz TOP i introduc
valoarea n noul vrf al stivei, operaiile de stocare extrag valoarea din vrful
curent al stivei i incrementeaz TOP.
Fiecare registru de date reprezint valoarea n precizie extins. Aceste
registre rein de obicei rezultate intermediare i utilizarea formatului extins
mbuntete acurateea rezultatului final.
Starea i coninutul fiecrui registru de date este indicat printr-o etichet
de doi bii. Deoarece sunt 8 registre de date, avem nevoie de 16 bii. Aceti 16 bii
sunt stocai n registrul de etichete (tag register).

8.2.2. Registre de control i stare


Acest grup conine trei registre de cte 16 bii:
registrul de control,
registrul de stare
registrul de etichete.
Registrul de stare
Acest registru de 16 bii pstreaz informaii de stare cu privire la
funcionarea unitii de calcul n virgul mobil.
15

0
C
3

TOP

C
2

C
1

C
0

E
S

S
F

P
E

U
E

O
E

Z
E

D
E

I
E

Figura 8.2 Structura registrului de stare FPU

Codurile de condiii C0 C3 indic rezultate ale operaiilor de comparaie


i aritmetice n virgul mobil. Unii sunt similari anumitor indicatori de stare din
registrul EFLAGS. Tabelul 8.3 arat corespondena dintre acetia i unii indicatorii
de stare:
Tabelul 8.3 Corespondena indicatori FPU indicatori CPU

Indicator FPU
C0
C2
C3

Indicator CPU
CF
PF
ZF

Ei sunt utilizai pentru salturi condiionate, asemenea biilor CPU echivaleni.


Pentru a-i putea testa trebuie mai nti copiai n registrul EFLAGS. Copierea se
face n dou etape. Mai nti se ncarc cuvntul de stare n registrul AX cu ajutorul
instruciunii FSTSW, apoi copiem aceste valori n registrul EFLAGS cu
instruciunea SAHF. Odat ncrcai, putem folosi n mod obinuit instruciunile de
salt condiionat. Acesta este considerat vechiul mecanism de efectuare a salturilor
condiionate pentru valori n virgul mobil, deoarece, ncepnd cu familia de
procesoare P6, mecanismul salturilor condiionate pentru valori n virgul mobil a
fost simplificat. Intel a pus la dispoziie o familie de instruciuni care compar
numerele n virgul mobil i seteaz direct indicatorii ZF, PF i CF din EFLAGS.
Astfel, o singur instruciune a noului mecanism nlocuiete trei instruciuni
necesare vechiului mecanism.
Cmpul TOP, de trei bii, indic registrul din vrful stivei de registre.
Valoarea 000 semnaleaz stiva goal, valoarea 111 stiva plin. Primul element
ncrcat are indice 0. O nou ncrcare incrementeaz indicii registrelor de date, o
extragere decrementeaz indicii. Pentru a permite adresarea circular a celor 8
registre, rezultatul incrementrii/decrementrii este trunchiat la trei bii (modulo 8).
Excepii n virgul mobil
n timpul efecturii unor operaii n virgul mobil pot aprea cteva tipuri
de erori, ncepnd de la simple erori logaritmice pn la erori provenite din limitele
reprezentrii. Erorile n virgul mobil se numesc excepii. Proiectanii au clasificat
excepiile n ase clase. Biii 0 5 din registrul de stare corespund acestor situaii
deosebite.
Operaie invalid operandul este invalid pentru operaia care trebuie
efectuat. De obicei sunt erori de algoritm: un rezultat nedefinit ca urmare a unei
mpriri zero la zero sau infinit la infinit. n acest caz, rezultatul este reprezentat
printr-o secven QNaN. FPU seteaz bitul IE (Invalid operation Exception).
Tot din clasa excepiilor de operaii invalide face parte i depirea
superioar sau inferioar a stivei de registre. Depirea superioar nseamn c o
instruciune ncearc s introduc un operand din memorie ntr-un registru de date
deja plin. Un registru plin este definit ca fiind registrul care conine o valoare de 0
(eticheta 01 din registrul de etichetare), o valoare valid (eticheta 00) sau o valoare
special (eticheta 10). Depirea inferioar se refer la cazul n care o instruciune
folosete ca operand surs un registru de date gol (incluznd ncercarea scrierii n
memorie a unui registru de date gol). Un registru gol (neutilizat) are o etichet
egal cu 11. Atunci cnd apar condiii de depire, programul nu poate continua.
Aadar, excepia de operaie invalid poate aprea ca urmare a operaiilor
aritmetice sau a celor cu stiva. Bitul SF (Stack Fault) d informaii cu privire la

cauza operaiei invalide. Dac acest bit este 1, operaia invalid a fost rezultatul
unei depiri a stivei de registre; n caz contrar, o instruciune aritmetic a ntlnit
un operand invalid. Atenie, FPU seteaz bitul SF la detecia unei condiii de
depire, dar nu-l terge cnd detecteaz o condiie de operand invalid. Ca rezultat,
dac nu a fost ters explicit de la ultima condiie de depire a stivei de registre,
starea indicatorului SF poate fi 1 i n cazul unei excepii aritmetice. n acest caz,
natura erorii este indicat de codul de condiie C1: depire superioar (C1 = 1) sau
depire inferioar (C1 = 0).
mprire cu zero dempritul este un numr finit nenul i mpritorul
este zero; sau, mai general spus, cnd rezultatul unei operaii cu numere finite este
infinit. Rezultatul este reprezentat corect, ca infinit cu semn. FPU seteaz bitul ZE
(Zero divide Exception).
Depire superioar rezultatul unei operaii este prea mare pentru a
putea fi reprezentat (depete marginea superioar a domeniului de reprezentare).
Rezultatul este reprezentat ca infinit. FPU seteaz bitul OE (Overflow Exception).
Depire inferioar rezultatul unei operaii este prea mic pentru a putea
fi reprezentat (depete marginea inferioar a domeniului de reprezentare).
Rezultatul este reprezentat ca zero. FPU seteaz bitul UE (Underflow Exception).
Operand denormalizat unul din operanzi nu este normalizat sau rezultatul
nu se poate reprezenta normalizat (de exemplu, rezultatul este att de mic nct este
imposibil normalizarea lui). FPU seteaz bitul DE (Denormal Exception).
Rezultat inexact rezultatul operaiei este inexact din cauza unei rotunjiri.
Aceast excepie de precizie apare cnd FPU nu poate reprezenta exact rezultatul
unei instruciuni n virgul mobil. Cazuri de genul mpririi 1/3, ce furnizeaz un
rezultat care se repet la infinit (0.33..3), sau cnd un numr real este convertit la
un format cu o precizie mai mic i se pierd bii prin aceast conversie. FPU
seteaz bitul PE (Precision Exception). De obicei, aceast excepie este mascat (n
registrul de control) deoarece rotunjirea sau trunchierea rezultatului este
satisfctoare.
Biii de excepie se pstreaz pn cnd programatorul ncarc registrul de
stare cu o valoare nou. Acest lucru permite programatorului s scrie o secven de
instruciuni i s plaseze un singur test de detecie al erorilor la sfritul acesteia, n
loc s scrie un test dup fiecare instruciune.

Registrul de control
Registrul de control permite programatorului s controleze cteva opiuni
de prelucrare.
15

RC

PC

P U O Z D I
M M M M M M

Figura 8.3 Structura registrului de control

Atunci cnd apare o excepie unitatea n virgul mobil are dou


posibiliti: s genereze o ntrerupere sau s o trateze intern. Modul intern de
tratare a excepiilor a fost deja specificat n seciunea anterioar. ntreruperea este
generat ca urmare a unei opiuni specificate de programator prin intermediul
primilor ase bii mai puin semnificativi din acest registru:
PM (Precision Mask) ntrerupere pentru semnalarea rotunjirii.
UM (Underflow Mask) ntrerupere pentru semnalarea depirii
inferioare.
OM (Overflow Mask) ntrerupere pentru semnalarea depirii
superioare.
ZM (Zero divide Mask) ntrerupere pentru semnalarea mpririi cu
zero.
DM (Denormalized operand Mask) ntrerupere pentru semnalarea
unui operand denormalizat.
IM (Invalid operation Mask) ntrerupere pentru semnalarea operaiei
invalide.
Dac aceti bii nu sunt mascai (valoare 1), se genereaz ntreruperea
corespunztoare; altfel, nu se genereaz excepia i programul continu cu execuia
urmtoarei instruciuni.
Metoda de rotunjire
RC (Rounding Control) controleaz modul de rotunjire a valorilor ce nu pot fi
reprezentate exact:
00 rotunjire la cel mai apropiat numr reprezentabil;
01 rotunjire inferioar (ctre -);
10 rotunjire superioar (ctre +);
11 trunchiere.
Valoarea implicit pentru acest cmp este 00.

Precizia de calcul
PC (Precision Control) specific formatul utilizat pentru reprezentarea rezultatului
operaiilor de +, -, , / i extragerea rdcinii ptrate:
00 simpl precizie;
01 neutilizat;
10 dubl precizie;
11 precizie extins, utilizat implicit.
Registrul de etichete
Acest registru conine 8 cmpuri de cte doi bii, fiecare cmp
corespunznd unui registru de date. T0 este cmpul pentru registrul 0, nu pentru
ST0, care, la un moment dat, poate fi oricare dintre cele 8 registre. Fiecare cmp
ofer informaii despre coninutul registrelor respective:
00 registrul conine un numr corect n virgul mobil;
01 registrul conine valoarea 0.0;
10 registrul conine valoarea infinit, un numr denormalizat sau
incorect;
11 registrul este gol (neutilizat).
Registrul de etichete nu poate fi utilizat direct de ctre programator, dar poate fi
folosit depanatoare de exemplu, ca s examineze i s interpreteze adecvat
coninutul registrelor de date.

8.3. Instruciuni n virgul mobil


FPU conine un set de instruciuni n virgul mobil pentru transferul de
date, operaii aritmetice, operaii de comparare i transcedentale. Pe lng acestea
sunt prezente instruciuni care ncarc n stiv constante uzuale precum sau
cuvinte de control.

8.3.1. Transferul datelor n virgul mobil


Setul instruciunilor de transfer conine instruciuni capabile s transfere
operanzi ntre registrele stivei i ntre vrful curent al stivei i memorie. Putem
mpri aceste instruciuni n dou categori: de ncrcare i de memorare.
Formatul general al instruciunilor de ncrcare este:

fld surs
Aceast instruciune ncarc (depune) operandul surs n registrul din vrful
stivei. Acest lucru are loc prin decrementarea indicatorului stivei i copierea
coninutului sursei n noul vrf al stivei. Operandul surs poate fi un registru al
stivei sau orice tip de numr real din memorie. nainte de ncrcarea n ST0,
operanzii din memorie n simpl sau dubl precizie sunt convertii automat la
formatul temporar de 80 de bii. Instruciunea FLD ST0 va copia valoarea din
vrful curent al stivei n noul vrf al stivei.
Unele instruciuni de acest tip nu primesc niciun operand. Ele introduc n
stiv anumite constante uzuale.
Instruciune
FLDZ
FLD1
FLDPI
FLDL2T
FLDL2E
FLDLG2
FLDLN2

Descriere
Introduce valoarea +0.0 n ST0
Introduce valoarea +1.0 n ST0
Introduce valoarea n ST0
Introduce valoarea !"#210 n ST0
Introduce valoarea !"#2! n ST0
Introduce valoarea !"#102 n ST0
Introduce valoarea ln 2 n ST0

Datorit faptului c reprezentarea acestora se face pe 10 octei, iar instruciunile se


reprezint numai pe doi octei, rezult c, pe lng faptul c simplific
programarea, acestea economisesc spaiu de memorie i mbuntesc viteza de
execuie.
Programul floatDec.asm arat cum putem definit n limbajul de asamblare
valori n virgul mobil.
;
;floatDef.asm
;
section .data
fs1 dd
0.0
fs2 dd
1.0
fs3 dd
12.34
fd1 dq
3.333333333
fd2 dq
4.444444444
fd3 dq
5.0
fd4 dq
1.234567e20
;dubl precizie
fd5 dq
1.e10
;10.000.000.000
fd6 dq
1.e+10
;sinonim cu 1.e10
fd7 dq
1.e-10
;0.000 000 000 1

fd8 dq
fd9 dq
fd10 dq
fe1 dt
fe2 dt
section .bss
m32 resd
m64 resq
section .text
global _start
_start:
nop
finit
fld dword
fld dword
fld qword
fld qword
fld qword
fld qword
fld tword
fld tword
fst dword
fst qword

12.34
3.14
3.141592653589793238462
3.141592653589793238462 ;pi
12.34
1
1

[fs3]
[fs2]
[fd1]
[fd2]
[fd3]
[fd5]
[fe1]
[fe2]
[m32]
[m64]

mov eax,1
mov ebx,0
int 80h
YASM accept constante n virgul mobil numai ca argumente pentru directivele
DD, DQ i DT. Ele sunt exprimate n forma tradiional: digii, urmai de punct, ali
digii opionali, apoi un E opional urmat de exponent. Punctul este obligatoriu,
astfel nct asamblorul s poat diferenia ntre DD 1, care declar un ntreg, de DD
1.0 care declar un numr n virgul mobil. n YASM, aproape orice instruciune
care adreseaz operanzi din memorie trebuie s indice mrimea corespunztoare cu
unul din prefixele DWORD, QWORD sau TWORD.
n GDB, valorile zecimale pot fi afiate cu opiunea f a comenzii x:
(gdb) x /1wf &fs1
0x80490cc <fs1>:
0
(gdb) x /1wf &fs2
0x80490d0 <fs2>:
1

(gdb) x /1wf &fs3


0x80490d4 <fs3>:
12.3400002
Valorile fs1, fs2, fs3 sunt valori n simpl precizie. Din ultima comand se
observ c erorile de rotunjire i fac apariia chiar i atunci cnd depanatorul
ncearc s calculeze valorile. Ca s afim valori n dubl precizie trebuie s
utilizm opiunea g, destinat valorilor de 8 octei.
(gdb) x /1gf &fd1
0x80490d8 <fd1>:
3.3333333330000001
(gdb) x /1gf &fd2
0x80490e0 <fd2>:
4.4444444440000002
(gdb) x /1gf &fd3
0x80490e8 <fd3>:
5
(gdb) x /1gf &fd4
0x80490f0 <fd4>:
1.234567e+20
(gdb) x /1gf &fd5
0x80490f8 <fd5>:
10000000000
(gdb) x /1gf &fd6
0x8049100 <fd6>:
10000000000
(gdb) x /1gf &fd7
0x8049108 <fd7>:
1e-10
(gdb) x /1gf &fd8
0x8049110 <fd8>:
12.34
(gdb) x /1gf &fd9
0x8049118 <fd9>:
3.1400000000000001
(gdb) x /1gf &fd10
0x8049120 <fd10>:
3.1415926535897931
Obsevai modul n care au fost rotunjite ultimele dou valori ale lui . Valorile n
precizie extins nu pot fi afiate direct.
Instruciunea FINIT iniializeaz unitatea n virgul mobil. FINIT
seteaz registrele de stare i control la valorile implicite i modific cmpurile de
etichete astfel nct s indice faptul c registrele de date FPU sunt goale. Dar,
ntotdeauna este un dar, dei modific etichetele, instruciunea FINIT nu
altereaz datele deja existente n aceste registre. Este obligaia programatorului s
in evidena registrelor de date folosite de program i dac conin date valide sau
nu.
Dup execuia primei instruciuni, valoarea din registrul ST0 poate fi
afiat cu comanda print $st0 sau info reg $st0.

(gdb) print $st0


$5 = 12.340000152587890625
(gdb) info reg $st0
st0
12.340000152587890625

(raw 0x4002c570a40000000000)

Valoarea locaiei de memorie fs1 a fost plasat n registrul ST0. A doua


instruciune nlocuiete coninutul lui ST0 cu valoarea fs2 i coboar fs1 n
registrul ST1.
(gdb) p $st0
$6 = 1
(gdb) p $st1
$7 = 12.340000152587890625
Instruciunea fst dword [m32]stocheaz n locaia de memorie de 32 de bii
m32 valoarea aflat n prezent n vrful stivei ST0. Acesta este i formatul general
al instruciunilor de memorare:
fst destinaie
unde destinaie poate fi un alt registru din stiv sau o locaie de memorie de
32 sau 64 de bii (conversia de la precizia extins se face automat). Dac vrful
stivei este etichetat special (adic conine o valoare de tip infinit, NaN sau numr
denormalizat) atunci nu se face convesia nici pentru exponent, nici pentru mantis.
Pentru a pstra valoarea special, acestea sunt trunchiate la dreapta, la dimensiunea
destinaiei. Un lucru foarte important de reinut, demonstrat de urmtoarele
comenzi, este c aceast instruciune nu extrage valoarea din stiv; doar o copiaz.
Dac doreti s copiezi i s extragi, adaugi sufixul p.
(gdb) p $st0
$8 = 12.340000000000000000138777878078145
(gdb) x /1wf &m32
0x804913c <m32>:
12.3400002
(gdb) p $st0
$8 = 12.340000000000000000138777878078145
(gdb) p $st1
$9 = 3.1415926535897932380791280904119844
(gdb) n
35
mov eax,1

(gdb) x /1gf &m64


0x8049140 <m64>:
12.34
(gdb) p $st0
$10 = 12.340000000000000000138777878078145
n continuare prezentm alte instruciuni de transfer.
FILD surs - convertete operandul din memorie de la formatul
ntreg binar la formatul real temporar i depune rezultatul n stiv.
FBLD surs convertete operandul din memorie de la formatul
zecimal mpachetat la formatul real temporar i depune rezultatul n
stiv.
FSTP destinaie realizeaz o operaie asemntoare cu FST, cu
deosebirea c extrage valoarea din stiva de registre. De asemenea,
FSTP permite plasarea n memorie a unui numr n precizie extins, n
timp ce FST nu accept asta. Instruciunea FSTP ST0 realizeaz
extragerea din stiv fr transfer n memorie.
FIST destinaie rotunjete coninutul registrului din vrful
stivei la un ntreg (n conformitate cu biii RC din registrul de control)
i transfer rezultatul la destinaie. Destinaia poate fi de 32 sau 64 de
bii. Zero negativ este memorat cu aceeai codificare ca zero pozitiv.
FISTP destinaie instruciune similar cu instruciunea
anterioar, dar n plus, gliseaz o dat stiva de registre. Destinaia
poate fi de orice tip ntreg.
FBSTP destinaie convertete valoarea din vrful stivei la un
ntreg zecimail mpachetat, depunde rezultatul n memorie i gliseaz
stiva de registre. Conversia se face prin rotunjire ntreag, prin
adunarea valorii 0.5 i apoi trunchierea rezultatului.
FXCH destinaie interschimb destinaia cu vrful stivei. Dac
destinaia nu este specificat explicit, se va utiliza implicit ST1.
Instruciunea constituie un mijloc eficient de utilizare a instruciunilor
n virgul mobil pe elementele existente n stiv. De exemplu,
secvena urmtoare extrage rdcina ptrat din al treilea registru din
stiv:
FXCH ST3
FSQRT
FXCH ST3
Urmtorul program demonstreaz cum pot fi utilizate valorile constante
prestabilite.
;

;fpuconsts.asm
;
section .text
global _start
_start:
nop
fld1
fldz
fldpi
fldl2t
fldl2e
fldlg2
fldln2
mov eax,1
mov ebx,0
int 80h
(gdb) info all
st0
0.6931471805599453094286904741849753
st1
0.30102999566398119522564642835948945
st2
1.4426950408889634073876517827983434
st3
3.3219280948873623478083405569094566
st4
3.1415926535897932385128089594061862
st5
0
(raw 0x00000000000000000000)
st6
1
(raw 0x3fff8000000000000000)
st7
0
(raw 0x00000000000000000000)
Valorile sunt plasate n ordinea invers introducerii lor.

8.3.2. Operaii aritmetice n virgul mobil


Aa cum era de ateptat, unitatea de calcul n virgul mobil pune la
dispoziie i instruciuni pentru realizarea funciilor matematice de baz cu valori n
virgul mobil. Funciile matematice de baz sunt descrise n urmtorul tabel.
Tabelul 8.4 Operaii aritmetice n virgul mobil

Instruciune
FADD
FSUB
FSUBR

Descriere
Adunare n virgul mobil
Scdere n virgul mobil
Scdere invers n virgul mobil

FMUL
FDIV
FDIVR

mnulire n virgul mobil


mprire n virgul mobil
mprire invers n virgul mobil

Aceste instruciuni permit minimizarea referinelor la memorie i optimizeaz


utilizarea stivei de registre. Fiecare poate mbrca o varietate de forme.
Adunarea
Instruciunea FADD poate fi utilizat n urmtoarele forme:
FADD surs adun la registrul ST0 un operand n simpl sau dubl
precizie aflat n memorie. Rezultatul rmne n ST0.
FADD ST(i),ST0 adun ST0 la ST(i) i stocheaz rezultatul n
ST(i).
FADD ST0,ST(i) adun ST(i) la ST0 i stocheaz rezultatul n
ST0.
FADDP ST(i),ST0 adun ST0 la ST(i), stocheaz rezultatul n
ST(i) i gliseaz (pop) ST0.
FADDP adun ST0 cu ST1, stocheaz rezultatul n ST1 i gliseaz
stiva de registre. Rezultatul se va afla n ST0. Valoarea iniial din ST1
este pierdut, valoarea din ST0 se va afla n ST7.
FIADD ntreg adun un ntreg de 16 sau 32 de bii la ST0 i
stocheaz rezultatul n ST0.
Urmtorul program demonstreaz afirmaiile anterioare.
;
;fadd.asm
;
section .data
fa
dd
2.5
fb
dd
5.5
fc
dd
8.0
fd
dd
7.0
a
dd
12
;ntreg de 32 de bii
section .text
global _start
_start:
nop
fld dword [fa]
fld dword [fb]
fld dword [fc]

fld dword [fd]


faddp
fadd dword [fc]
faddp st2,st0
fadd st0,st2
faddp
fiadd dword [a]
mov eax,1
mov ebx,0
int 80h
Executai primele patru instruciuni i observai poziia valorilor n stiva de
registre.
(gdb) info all
st0
7
(raw 0x4001e000000000000000)
st1
8
(raw 0x40028000000000000000)
st2
5.5
(raw 0x4001b000000000000000)
st3
2.5
(raw 0x4000a000000000000000)
st4
0
(raw 0x00000000000000000000)
Instruciunea FADDP adun ST0 cu ST1 (7 + 8), pstreaz rezultatul n ST1
(pierdem valoarea 8) i gliseaz ST0 (7). Registrul ST0 devine ST7 (7). ST1 (15)
devine vrful stivei de registre i i modific denumirea n ST0 (15). n urma
acestor operaii stiva de registre arat astfel:
st0
st1
st2
st3
st3
st4
st5
st6
st7

5.5
2.5
0
0
0
0
0

15
(raw 0x4002f000000000000000)
(raw 0x4001b000000000000000)
(raw 0x4000a000000000000000)
(raw 0x00000000000000000000)
(raw 0x00000000000000000000)
(raw 0x00000000000000000000)
(raw 0x00000000000000000000)
(raw 0x00000000000000000000)
7
(raw 0x4001e000000000000000)

Urmtoarea instruciune adun la ST0 un operand din memorie.


st0
st1
st2

5.5
2.5

23
(raw 0x4003b800000000000000)
(raw 0x4001b000000000000000)
(raw 0x4000a000000000000000)

st3

(raw 0x00000000000000000000)

Instruciunea FADDP ST2,ST0 adun ST0 la ST2, rezultatul rmne n ST2, i


gliseaz ST0. Aadar, ST2 devine ST1, iar ST0 devine ST7.
st0
st1
st2
st3
st4
st5
st6
st7

5.5
0
0
0
0
7

(raw 0x4001b000000000000000)
25.5
(raw 0x4003cc00000000000000)
(raw 0x00000000000000000000)
(raw 0x00000000000000000000)
(raw 0x00000000000000000000)
(raw 0x00000000000000000000)
(raw 0x4001e000000000000000)
23
(raw 0x4003b800000000000000)

Instruciunea FADD ST0,ST2 adun ST2 la ST0 i pstreaz rezultatul n ST0.


Nu descarc niciun registru. Ne dm seama din faptul c instruciunea nu este
urmat de sufixul P(OP).
st0
st1
st2

-nan(0xc000000000000000) (raw 0xffffc000000000000000)


25.5 (raw 0x4003cc00000000000000)
0
(raw 0x00000000000000000000)

Dac ne-am fi ateptat s rezulte o valoare de 5.5 ne-am nelat. Registrul ST2 nu
avea valoarea 0, ci era neutilizat (eticheta acestuia din registrul de etichete este 11).
Operaia de adunare ntre o valoare i un registru neutilizat a ntors o excepie de
tip operaie invalid; de aici rezult valoarea NaN n registrul ST0.
Urmtoarele dou instruciuni, folosind NaN ca operand, au ca rezultat aceeai
valoare.
Este foarte important s inem evidena strii registrelor de date. Fiecare
variant a instruciunilor aritmetice specific registrul, rolul su n operaie i dac
stiva de registre gliseaz sau nu. Nu trebuie s facem presupuneri, de cele mai
multe ori sunt greite.
Scderea
Instruciunea FSUB are un format similar cu instruciunea de adunare.
Instruciunea de scdere
fsub surs
execut operaia
ST0 = ST0 surs

Numai c, spre deosebire de adunare, scderea nu este comutativ (ST0


surs nu d acelai rezultat ca surs ST0). Dac avem nevoie de operaia
invers, surs ST0, folosim instruciunea FSUBR (FSUB Reverse).
fsubr surs
execut operaia

ST0 = surs ST0

Similar instruciunii de adunare, FSUB are i versiuni cu doi operanzi de tip


registru, cu sau fr posibilitate de glisare a stivei de registre.
FSUB ST(i),ST0 scade ST0 din ST(i) i stocheaz rezultatul n
ST(i).
FSUB ST0,ST(i) scade ST(i) din ST0 i stocheaz rezultatul n
ST0.
FSUBP scade ST0 din ST1, reine rezultatul n ST1 i gliseaz ST0.
FSUBP ST(i),ST0 scade ST0 din ST(i), stocheaz rezultatul n
ST(i) i gliseaz ST0.
FSUBRP ST(i),ST0 scade ST(i) din ST0, stocheaz rezultatul n
ST(i) i gliseaz ST0.
Pentru a scdea un ntreg, putem folosi FISUB, pentru scderea standard, sau
FISUBR, pentru cea invers. ntregul de 16 sau 32 de bii trebuie s fie n
memorie.
nmulirea
Instruciunea de nmulire are versiuni similare cu instruciunea de adunare:
FMUL surs nmulete la registrul ST0 un operand n simpl sau
dubl precizie aflat n memorie. Rezultatul rmne n ST0.
FMUL ST(i),ST0 nmulete ST0 la ST(i) i stocheaz rezultatul
n ST(i).
FMUL ST0,ST(i) nmulete ST(i) la ST0 i stocheaz rezultatul
n ST0.
FMULP ST(i),ST0 nmulete ST0 la ST(i), stocheaz rezultatul
n ST(i) i gliseaz ST0.
FMULP nmulete ST0 cu ST1, stocheaz rezultatul n ST1 i
gliseaz ST0.
FIMUL ntreg nmulete un ntreg de 16 sau 32 de bii la ST0 i
stocheaz rezultatul n ST0.

mprirea
Instruciunea de mprire are versiuni similare instruciunii de scdere.
Cteva din acestea sunt:
FDIV surs mparte coninutul registrului ST0 la surs i
pstreaz rezultatul n ST0 (ST0 = ST0/surs). Operandul surs
poate fi o valoare n simpl sau dubl precizie aflat n memorie.
FDIVR surs mparte coninutul sursei la registrul ST0 i
pstreaz rezultatul n ST0 (ST0 = surs/ST0).
FDIV ST(i),ST0 mparte ST(i) la ST0 i stocheaz rezultatul n
ST(i).
FDIV ST0,ST(i) mparte ST0 la ST(i) i stocheaz rezultatul n
ST0.
FDIVP mparte ST1 la ST0, reine rezultatul n ST1 i gliseaz ST0.
FDIVRP mparte ST0 la ST1, reine rezultatul n ST1 i gliseaz
ST0.
FDIVP ST(i),ST0 mparte ST(i) la ST0, stocheaz rezultatul n
ST(i), i gliseaz ST0.
FDIVRP ST(i),ST0 mparte ST0 din ST(i), stocheaz rezultatul
n ST(i) i gliseaz ST0.
FIDIV ntreg mparte ST0 la un ntreg de 16 sau 32 de bii aflat
n memorie.

8.3.3. Instruciuni transcedentale


Unitatea n virgul mobil pune la dispoziie mult mai multe funcii
matematice n virgul mobil dect simpla adunare, scdere, nmulire i mprire.
Grupul instruciunilor transcedentale realizeaz calcule mari consumatoare de timp.
Cuprinde toate funciile trigonometrice, hiperbolice, inversele acestora, logaritmice
i exponeniale. Toate aceste instruciuni opereaz asupra primului sau primelor
dou elemente din vrful stivei i returneaz rezultatul lor n stiv. Toi operanzii
trebuie s fie normalizai, ceilali sunt considerai invalizi. Dac un operand este
invalid, instruciunea va furniza un rezultat nedefinit, fr semnalarea unei excepii.
Urmtorul tabel prezint cteva din aceste funcii avansate.
Tabelul 8.5 Instruciuni transcedentale

Instruciune Descriere
FSIN
Calculeaz sinus din valoarea lui ST0
FCOS
Calculeaz cosinus din valoarea lui ST0

FSINCOS
FABS
FCHS
FSCALE
FSQRT
FRNDINT
F2XM1
FPATAN
FPTAN
FYL2X
FYL2XP1

Calculeaz att sin ct i cos din valoarea lui ST0


Calculeaz valoarea absolut din valoarea lui ST0
Schimb semnul valorii din ST0
Calculeaz ST0 la puterea ST1
Calculeaz rdcina ptrat a valorii din ST0
Rotunjete la cel mai apropiat ntreg valoarea lui ST0
Calculeaz 2 la puterea ST0, minus 1
nlocuiete ST1 cu arctan (ST1/ST0) i descarc ST0
nlocuiete ST0 cu tangenta sa i introduce 1 n stiva de registre
Calculeaz ST1 = ST1 * log2 ST0 i descarc (extrage) stiva
Calculeaz ST1 = ST1 * log2 (ST0+1.0)

8.3.4. Instruciuni de comparare


Din nefericire, compararea numerelor n virgul mobil nu este att de
facil precum aceea a numerelor ntregi. Cnd lucrm cu ntregi i dorim s aflm
dac o valoare este mai mare dect, egal cu, sau mai mare dect, este uor s
folosim instruciunea CMP i s evalum valorile din registrul de stare EFLAGS. n
cazul numerelor n virgul mobil nu putem folosi instruciunea CMP. Unitatea
FPU ofer propriile instruciuni pentru compararea acestora. Una este
fcom surs
Fcom compar valoarea din ST0 cu sursa i seteaz indicatorii de stare FPU.
Operandul surs poate fi n memorie sau ntr-un registru. Dac nu se d niciun
operand, instruciunea FCOM compar registrul ST0 cu registrul ST1. Mai multe
variante ale instruciunii sunt date n Tabelul 8.6.
Tabelul 8.6 Instruciuni de comparare n virgul mobil

Instruciune
FCOM surs
FCOM
FCOM ST(i)
FCOMP
FCOMP ST(i)
FCOMP surs
FCOMPP
FTST
FICOM ntreg

Descriere
Compar ST0 cu o valoare de 32 sau 64 de bii din memorie
Compar registrul ST0 cu registrul ST1
Compar registrul ST0 cu alt registrul de date
Compar ST0 cu ST1 i gliseaz stiva de registre
Compar ST0 cu alt registru de date i gliseaz stiva de registre
Compar ST0 cu o valoare din memorie i gliseaz stiva
Compar ST0 cu ST1 i gliseaz stiva de dou ori
Compar registrul ST0 cu valoarea 0.0
Compar ST0 un ntreg de 16 sau 32 de bii

Rezultatul comparaiei seteaz biii codurilor de condiii C0, C2 i C3, din registrul

de stare.
Condiie
ST0 > surs
ST0 < surs
ST0 = surs

C3
0
0
1

C2
0
0
0

C0
0
1
0

Aa cum am menionat nainte, C1 indic condiia de depire a stivei. Totui,


acesta este utilizat de o instruciune din aceast familie, instruciunea FXAM.
Aceasta examineaz numrul din ST0 i ncarc bitul de semn n indicatorul C1 (0
pozitiv, 1 negativ). n plus, specific tipul numrului prin intermediul celorlali trei
bii de condiie.
Tip
Neacceptat
NaN
Normalizat
Infinit
Zero
Gol
Denormalizat

C3
0
0
0
0
1
1
1

C2
0
0
1
1
0
0
1

C0
0
1
0
1
0
1
1

Tipul neacceptat este un format care nu face parte din standardul IEEE 754.
Celelalte tipuri au fost deja ntalnite pe parcurs.
Pentru a determina starea biilor de condiie (rezultatul comparaiei) trebuie
s copiem valoarea registrului de stare n registrul AX sau la o locaie de memorie
cu instruciunea FSTSW, i apoi s o ncrcm n registrul EFLAGS cu
instruciunea SAHF.
;
;fcom.asm
;
section .data
fs1 dd
1.923
fs2 dd
4.5532
section .text
global _start
_start:
nop
fld dword [fs1]
fcom dword [fs2]
fstsw AX

sahf
ja greater
jb lessthan
mov eax,1
mov ebx,0
int 80h
greater:
mov eax,1
mov ebx,2
int 80h
lessthan:
mov eax,1
mov ebx,1
int 80h
Instruciunea SAHF mut biii 0, 2, 4, 6 i 7 din AH pe poziia biilor CF, PF, AF,
ZF i SF din registrul EFLAGS. Acest mapare a biilor din registrul de stare FPU
la respectivii bii EFLAGS este una intenionat. Aadar, instruciunea FSTSW
urmat de SAHF are ca rezultat:
mutarea bitului C0 pe poziia CF
mutarea bitului C2 pe poziia PF
mutarea bitului C3 pe poziia ZF
n acest moment putem determina rezultatul comparaiei prin aceleai folosite
pentru numerele ntregi, JA, JB i JZ.
Programul fcom.asm produce rezultate diferite n funcie de valorile setate
n memorie. Codul rezultat poate fi observat cu ajutorul comenzii echo:
./fcom
echo $?
1
Rezultatul 1 indic faptul c prima valoare fs1 este mai mic dect valoarea fs2.
Putei modifica valorile din program astfel nct s v asigurai c acesta
funcioneaz corect.
ncepnd cu procesoarele Pentium P6, Intel a pus la dispoziie un nou
mecanism de comparare: familia de instruciuni FCOMI. Instruciunea FCOMI i
variantele sale realizeaz comparaii n virgul mobil i indic rezultatul acestora
direct prin biii CF, PF i ZF din registrul EFLAGS.

Tabelul 8.7 Instruciuni din familia FCOMI

Instruciune
FCOMI
FCOMIP
FUCOMI
FUCOMIP

Descriere
Compar registrul ST0 cu registrul ST(i)
Compar ST0 cu registrul ST(i) i gliseaz stiva de registre
Verific corectitudinea valorilor nainte de comparare
Verific corectitudinea valorilor nainte de comparare i gliseaz
stiva

Aa cum reiese din Tabelul 8.7, o limitare a instruciunilor FCOMI este c pot
compara numai valori din registrele de date FPU, nu i un registru de date cu o
valoare din memorie. ns ultimele dou instruciuni ofer un serviciu indisponibil
instruciunilor din familia FCOM. FUCOMI i FUCOMIP verific faptul c
valorile ce vor fi comparate se regsesc ntr-un format valid (folosesc registrul de
etichetare). Dac este prezent o valoare nenormalizat se ntoarce o excepie.
Rezultatul instruciunilor FCOMI asupra biilor din registrul EFLAGS au
urmtoarele semnificaii:
Condiie
ST0 > ST1
ST0 < ST1
ST0 = ST1
;
;fcomi.asm
;
section .data
fs1 dd
1.4444
fs2 dd
4.5532
section .text
global _start
_start:
nop
fld dword [fs2]
fld dword [fs1]
fcomi st0,st1
ja greater
jb lessthan
mov eax,1
mov ebx,0
int 80h
greater:

ZF
0
0
1

PF
0
0
0

CF
0
1
0

mov eax,1
mov ebx,2
int 80h
lessthan:
mov eax,1
mov ebx,1
int 80h
Deoarece instruciunea FCOMI compar numai valori din registre FPU, valorile
din memorie sunt ncrcate n ordine invers, astfel nct, la momentul comparrii,
valoarea fs1 s se afle n registrul ST0.

8.3.5. Instruciuni FPU de transfer condiionat


Similar instruciunilor de transfer condiionat pentru ntregi (CMOV),
instruciunile FCMOV permit transferul valorilor n virgul mobil n funcie de
anumite condiii. Toate instruciunile din familia FCMOV mut un registru ST(i) n
registrul ST0 pe baza condiiilor de adevr prezente n registrul EFLAGS.
Deoarece operaia se bazeaz pe registrul EFLAGS, se obinuiete ca instruciunea
FCMOV s fie precedat de o instruciune FCOMI. Tabelul 8.8 prezint
instruciunile familiei FCMOV.
Tabelul 8.8 Instruciuni n virgul mobil de transfer condiionat

Instruciune
FCMOVB
FCMOVE
FCMOVBE
FCMOVU
FCMOVNB
FCMOVNE
FCMOVNBE
FCMOVNU

Descriere
Mut dac ST0 este mai mic dect ST(i) (CF=1)
Mut dac ST0 este egal cu ST(i) (ZF=1)
Mut dac ST0 este mai mic sau egal cu ST(i) (CF=1 sau ZF=1)
Mut dac ST0 este neordonat (PF=1)
Mut dac ST0 nu este mai mic dect ST(i) (CF=0)
Mut dac ST0 nu este egal cu ST(i) (ZF=0)
Mut dac ST0 nu este mai mic sau egal cu ST(i) (CF=0 sau ZF=0)
Mut dac ST0 nu este neordonat (PF=0)

Instruciuni de control
Aceste instruciuni se folosesc pentru activiti de iniializare, gestionare a
excepiilor i comutare de proces. Ele permit salvarea strii curente a unitii de
calcul n virgul mobil (contextul FPU) i revenirea la aceasta dup ncheierea
altui proces.
Una din aceste instuciuni este FSTENV, utilizat pentru stocarea ntr-o

zon de memorie a ntregului context FPU. Sunt salvate urmtoarele informaii:


registrul de control,
registrul de stare,
registrul de etichete,
valoarea indicatorului de instruciune FPU,
valoarea indicatorului de date FPU,
valoarea ultimului cod operaional FPU executat.
Valorile sunt stocate ntr-un bloc de memorie de 28 de octei. Instruciunea
FLDENV efectueaz procesul invers, ncarc valorile din blocul de memorie napoi
n unitatea FPU. Urmtorul program demonstreaz efectul acestor instruciuni.
;
;fpuenv.asm
;
section .data
fs1 dd
34.78
fs2 dd
78.34
fs3 dd
100.1
fs4 dd
200.1
w
dw
0b7fH
section .bss
buffer
resb 28
section .text
global _start
_start:
nop
finit
fld dword [fs1]
fld dword [fs2]
fldcw [w]
fstenv [buffer]
finit
fld dword [fs3]
fld dword [fs4]
fldenv [buffer]
mov eax,1
mov ebx,0
int 80h
Programul fpuenv.asm iniializeaz unitatea FPU, ncarc cteva valori n registrele

de date FPU, modific cmpul biilor de rotunjire din registrul de control i


stocheaz contextul FPU n cei 28 de octei ai locaiei de memorie buffer.
nainte de instruciunea FSTENV registrele FPU arat astfel:
(gdb) info all
st0
78.339996337890625
(raw 0x40059cae140000000000)
st1
34.779998779296875
(raw 0x40048b1eb80000000000)
st2
0
(raw 0x00000000000000000000)
st3
0
(raw 0x00000000000000000000)
st4
0
(raw 0x00000000000000000000)
st5
0
(raw 0x00000000000000000000)
st6
0
(raw 0x00000000000000000000)
st7
0
(raw 0x00000000000000000000)
fctrl
0xb7f 2943
fstat
0x3000
12288
ftag
0xfff 4095
Observai valorile registrelor de control, stare i etichete. Aceleai valori sunt
prezente n buffer dup execuia instruciunii FSTENV:
(gdb) x /28xb &buffer
0x80490d4 <buffer>: 0x7f
0xff
0x80490dc <buffer+8>: 0xff
0x08
0x80490e4 <buffer+16>:
0x04 0x08
0x80490ec <buffer+24>:

0x0b 0xff

0xff

0x00 0x30 0xff

0x0f 0xff

0xff

0x8a

0x80

0x04

0x00

0x00

0x05

0x01

0xc4

0x90

0x00

0x00

0xff

0xff

Dup salvarea strii curente, unitatea FPU este iniializat i se introduc din nou
cteva valori de date. Privii valorile nregistrate de registrele FPU nainte i dup
execuia instruciunii FLDENV. Observai c n urma execuiei FLDENV valorile
registrelor de date ST0...ST7 nu au fost restaurate, dar registrele de control, stare i
etichete indic valorile dinainte de instruciunea FSTENV.
Instruciunea FSTENV stocheaz contextul FPU, dar nu i valorile
registrelor de date. Salvarea context FPU plus date se face cu instruciunea FSAVE.
Instruciunea FSAVE copiaz ntr-o locaie de memorie de 108 octei toate
registrele interne ale unitii FPU (inclusiv registrele de date), dup care o
reiniializeaz. La restaurarea cu instruciunea FRSTOR, toate registrele sunt
readuse la valoarea dinaintea execuiei FSAVE.
;

;fpusave.asm
;
section .data
fs1 dd
34.78
fs2 dd
78.34
fs3 dd
100.1
fs4 dd
200.1
w
dw
0b7fH
section .bss
buffer
resb 108
section .text
global _start
_start:
nop
finit
fld dword [fs1]
fld dword [fs2]
fldcw [w]
fsave [buffer]
fld dword [fs3]
fld dword [fs4]
frstor [buffer]
mov eax,1
mov ebx,0
int 80h
n urma execuiei FSAVE, zona de memorie nu conine numai valorile din
registrele de control, stare i etichete, ci i pe cele ale registrelor de date. De
asemenea, FSAVE realizeaz automat iniializarea unitii FPU (de aceea acest
program nu conine al doilea FINIT). Dup execuia instruciunii FRSTOR putei
observa c toate registrele sunt readuse la starea dinaintea FSAVE.
Alte instruciuni din aceast categorie sunt prezentate pe scurt n Tabelul
8.9. Multe din instuciunile de control ale unitii FPU au dou forme:
form wait, prefixat numai cu litera F, de exemplu FINIT.
form non-wait, prefixat cu FN, de exemplu FNINIT.
Termenii wait i non-wait se refer la modul n care instruciunile trateaz
excepiile n virgul mobil. Excepiile au fost discutate anterior n seciunea
dedicat registrului de stare. Instruciunile n virgul mobil pot genera ase tipuri

de excepii. De obicei, acestea semnalizeaz apariia unei erori aritmetice.


Majoritatea instruciunilor ateapt (sunt ntrziate) s verifice dac nu
cumva instruciunea anterioar returneaz o excepie. Dac este prezent o
excepie, aceasta trebuie tratat nainte de execuia urmtoarei instruciuni.
Alternativ, unele instruciuni au versiuni non-wait, care nu ateapt s verifice
apariia unei excepii n virgul mobil. Aceste instruciuni permit programului s
salveze sau s reseteze starea curent a unitii FPU fr a trata excepii.
Tabelul 8.9 Instruciuni de control FPU

Instruciune
FINIT/FNINIT
FCLEX/FNCLEX
FSAVE/FNSAVE
FRSTOR
FSTENV/FNSTENV
FLDENV
FSTCW/FNSTCW
FSTSW/FNSTSW
FLDCW
FDECSTP
FINCSTP
FFREE ST(i)
FNOP

Descriere
Iniializeaz unitatea FPU
terge toi indicatorii de excepie din cuvntul de stare
Salveaz n memorie contextul complet al unitii FPU
ncarc din memorie contextul complet al unitii FPU
Salveaz n memorie contextul FPU
ncarc contextul FPU din memorie
Salveaz n memorie cuvntul de stare curent
Salveaz cuvntul de stare curent n AX sau ntr-o locaie de
memorie
ncarc cuvntul de stare din memorie
Decrementeaz cmpul TOP din cuvntul de stare. Dac
TOP este 0, prin decrementare devine 7.
Incrementeaz cmpult TOP. Dac TOP este 7, devine 0.
Marcheaz ST(i) ca neutilizat
Echivalentul NOP de la numere ntregi

8.4. Exerciii
8.1. Convertii manual urmtoarele numere n format IEEE 754 de 32 de bii.
a)
b)
c)
d)

1.1
-0.1
2005.0
0.0039

e) -2015.3125
f) 0.33
g) -0.67
h) 3.14

8.2. Convertii manual n numere zecimale urmtoarele valori date n format IEEE
754 simpl precizie.
a) 4000 0000

e) c180 4000

b) bf80 0000
c) 3d80 0000
d) c259 48b4

f) 42f6 e666
g) 3f99 999a
h) 42c8 1000

8.3. Urmtorul program calculeaz rdcinile unei ecuaii de gradul al II-lea,


!! ! + !" + ! = 0.
Cele dou rdcini sunt definite astfel:
! + ! ! 4!"
,
2!
! ! ! 4!"
!1 =
,
2!
!1 =

Rdcinile sunt reale dac ! ! 4!", n caz contrar, rdcinile sunt imaginare.
Rulai programul i observai efectul fiecrei instruciuni. Modificai valorile
coeficienilor i, pentru fiecare caz, la finalul programului, afiai rdcinile cu
ajutorul comenzii x /1gf &r1, respectiv &r2.
section .data
a dq 2.0
b dq -3.0
c dq 1.0
section .bss
r1 resq 1
r2 resq 1
real resb 1
section .text
global _start
_start:
nop
finit
fld qword [a]
fadd ST0
fld qword [a]
fld qword [c]
fmulp ST1
fadd ST0
fadd ST0
fchs

fld qword [b]


fld qword [b]
fmulp ST1
faddp ST1
ftst
fstsw AX
sahf
jb no_real_roots
fsqrt
fld qword [b]
fchs
fadd ST1
fdiv ST2
fstp qword [r1]
fchs
fld qword [b]
fsubp ST1
fdivrp ST1
fstp qword [r2]
mov al,1
mov [real],al
jmp sfarsit
no_real_roots:
mov al,0
mov [real],al
sfarsit:
mov eax,1
mov ebx,0
int 80h

9. FUNCII

Programele prezentate pn acum au fost alctuite dintr-un singur modul


(prin modul nelegnd o singur unitate logic de prelucrare). Acest lucru a fost
posibil deoarece problemele au fost simple. Numai problemele simple pot fi
rezolvate ntr-o singur secven de cod. De obicei, pentru rezolvarea unor
aplicaii complexe, trebuie s descompunem problema n subprobleme mai simple,
relativ independente, scriind module de program distincte pentru fiecare din
acestea. De asemenea, dac o aplicaie necesit rularea repetat a unei secvene
de instruciuni, n loc s scriem de mai multe ori acelai cod este indicat s
introducem secvena respectiv ntr-un modul separat ce poate fi apelat de oriunde
din program. Modulele prelucrez valori de intrare primite din program (numit
program principal) i returneaz rezultate. Acest capitol prezint posibilitile de
care dispune un programator n limbaj de asamblare pentru a crea module
dedicate unor procese simple, regulile pe care trebuie s le respecte n elaborarea
acestora, precum i modul de interaciune a modulelor cu programul principal.

9.1. Modularizarea programelor


n matematic, putem obine funcii orict de complexe prin compunerea
unor funcii elementare (funcia polinomial, funcia de ridicare la putere sau de
extragere radical, etc.). n programare, putem construi programe orict de
complexe prin tehnica de modularizare, adic prin nlnuirea logic a unor uniti
elementare de program: citirea datelor de intrare (modulul de citire a datelor),
prelucrarea lor (modulul de prelucrare), afiarea rezultatelor obinute n urma
prelucrrii (modulul de afiare a rezultatelor). Mulimea valorilor care constituie
datele de intrare i datele de ieire ale subprogramului formeaz lista parametrilor
de intrare, respectiv de ieire. Din acest punct de vedere, n programare ntlnim
dou categorii de subprograme:
proceduri subprograme care primesc ca date de intrare oricte valori i
returneaz ca rezultat o singur valoare, mai multe valori, sau niciuna
(poate efectua o prelucrare care s nu aib ca efect obinerea unei valori);
funcii subprograme care primesc ca date de intrare oricte valori, dar
returneaz ca rezultat o valoare i numai una.

Teoretic, o funcie returneaz ntotdeauna o valoare. Cu toate acestea,


unele limbaje de programare, cum ar fi C, dispun i de funcii fr tip, care nu
returneaz nicio valoare. n aceste condiii, diferenele dintre proceduri i funcii
ncep s devin discutabile. n continuare, pentru consistena cu limbajul C, vom
folosi noiunea de funcie, singura admis de acesta. Putei considera procedura ca
fiind un termen generic pentru funcie.

9.2. Apelarea funciilor


n C, nsui programul principal este o funcie cu nume rezervat (main).
Deoarece nu numai programul principal admite funcii, ci i o funcie oarecare
poate avea, la rndul su, alte funcii, orice program care apeleaz o funcie se
numete program apelant. Prin apelarea unei funcii de ctre un program apelant
nelegem o comand pe care programul apelant o trimite funciei, comand prin
care i cere acesteia s execute operaiile din blocul su de instruciuni.
O funcie poate fi apelat ori de cte ori este nevoie i conine tot codul
necesar realizrii complete a unui proces. Nu necesit asisten din partea
programului apelant. Parametrii (datele de intrare) cu care lucreaz funcia pot fi
diferii de la apel la apel i sunt furnizai acesteia din interiorul programului
apelant.
_start:

call <nume_funcie>

exit
<nume_funcie>:

ret
Figura 9.1 Apelarea funciilor

La apelul unei funcii, execuia programului sare la prima instruciune a acesteia.


Procesorul execut instruciunile funciei pn cnd ntlnete instruciunea RET.
RET red controlul programului apelant din locul n care a fost apelat funcia.

9.3. Definirea funciilor


Crearea unei funcii n limbajul de asamblare presupune trei etape:
definirea valorilor de intrare necesare acesteia,
definirea operaiilor care trebuie efectuate asupra valorilor de intrare,
definirea modului de obinere i transmitere ctre programul apelant a
valorilor de ieire.
Modul n care se definete o funcie depinde de asamblor. n YASM, trebuie numai
s dm un nume secvenei de instruciuni respective. Odat creat, rutina poate fi
accesat de oriunde din programul principal prin instruciunea CALL. Instruciunea
CALL are un singur operand, numele funciei.
Urmtorul program folosete o funcie pentru a interschimba valorile din
dou registre.
;
;functie1.asm
;
section .data
temp dd 0
section .text
global _start
_start:
nop
mov eax,3
mov ebx,4
call _functie
mov
mov
int
_functie:
mov
mov
mov
ret

eax,1
ebx,0
080h
[temp],ebx
ebx,eax
eax,[temp]

9.4. Transferul controlului


n programul anterior, codul funciei a fost plasat la sfritul programului
principal. Textul funciei poate fi poziionat la fel de bine i la nceput, imediat
dup directiva section .text. Cnd am discutat despre rolul etichetei
_start am precizat c editorul de legturi folosete aceast etichet pentru
aflarea instruciunii de nceput a programului (instruciunea cu care trebuie s
nceap execuia programului). nainte de _start putem avea orice numr de
funcii fr ca execuia programului s fie afectat n vreun fel. n plus, spre
deosebire de unele limbaje de nivel nalt, n asamblare, funciile nu trebuie definite
nainte de apelare. Tot ceea ce caut instruciunea CALL este eticheta care specific
adresa de nceput a funciei, adres ce va fi ncrcat n registrul indicator de
instruciune.
Funcia din programul urmtor calculeaz complementul fa de doi pentru
valorile 3 i 5, rezultatul regsindu-se n registrul EDX. Complementul fa de doi
se obine cu instruciunea NEG inverseaz biii i adun 1. Dac vrem s obinem
complementul fa de unu, folosim instruciunea NOT, actualmente comentat.
;
;funcie2.asm
;
section .bss
mem resd 1
rez resd 1
section .text
functie:
;
not dword [mem]
neg dword [mem]
mov edx,[mem]
ret
global _start
_start:
nop
mov dword [mem],3
call functie
mov dword [mem],5
call functie
mov eax,1
mov ebx,0

;complementul fa de unu
;complementul fa de doi

int 080h
Instruciunea CALL, la apelare, salveaz n stiv coninutul registrului EIP
i l iniializeaz cu adresa de nceput a funciei. Numele acesteia este nlocuit de
asamblor cu deplasamentul calculat ntre locaia lui CALL i locaia funciei.
Aadar, n programul anterior, numele functie este un deplasament. Acest
deplasament, dup salvarea coninutului registrului EIP n stiv, este adunat la
valoarea EIP. Se ajunge astfel la nceputul funciei. Evident, valoarea
deplasamentului poate fi negativ sau pozitiv, n funcie de poziia funciei n
program. Instruciunea RET restabilete n EIP adresa salvat de instruciunea
CALL.

9.5. Metode de transfer al parametrilor


De obicei, funciile au nevoie de parametri de intrare. O funcie poate primi
parametri de intrare din programul apelant prin trei metode:
Prin intermediul registrelor;
Prin intermediul variabilelor globale;
Prin intermediul stivei.

9.5.1. Prin registre


Aa cum reiese din nume, metoda transferului prin registre presupune
folosirea unor registre de uz general. nainte de apelul funciei, programul
principal introduce toi parametrii necesari acesteia n anumite registre.
;
;regTr.asm
;
section .data
d1 dd 3
d2 dd 5
section .bss
temp resd 1
section .text
;definim functiile
global _functie
_functie:
mov [temp],ebx

mov ebx,eax
mov eax,[temp]
;revenire din funcie n programul principal
ret
;nceputul programului principal
global _start
_start:
nop
mov eax,[d1]
mov ebx,[d2]
;intrare n funcie
call _functie
;revenire din funcie
mov [d1],eax
mov [d2],ebx
mov eax,1
mov ebx,0
int 080h
Se observ cum funcia primete parametri de intrare de la programul
principal prin registrele EAX i EBX. Rezultatul este returnat programului prin
aceleai registre.
Aceast metod este rapid, toate argumentele se gsesc n registre. ns,
dac folosim registre i n corpul funciei, nu exist nicio garanie c valoarea
acestora va fi identic cu cea de la intrarea n funcie. Altfel spus, e posibil ca
valorile registrelor de la ieirea din funcie s difere de cele de pn la intrarea n
funcie. Din acest motiv este indicat s urmrim registrele folosite de funcie pentru
procesul ei intern. Att registrele ct i locaiile de memorie folosite n interiorul
funciei pot avea alt valoare la rentoarcerea n program. Dac o funcie modific
registrele folosite de programul principal este crucial s salvm coninutul lor
nainte de apelul funciei. Salvarea se poate face cu instruciunile PUSH, PUSHA.
Restaurarea cu POP, POPA. De asemenea, trebuie s fim ateni care sunt registrele
folosite de funcie pentru returnarea rezultatului. Dac acestea sunt suprascrise,
funcia a fost efectuat inutil.
Din moment ce procesorul nu dispune de foarte multe registre de uz
general, un dezavantaj implicit al metodei este numrul mic de argumente care
poate fi furnizat funciei.
Dezavantajele modalitii de transmitere a parametrilor prin intermediul
registrelor:
neconvenabil - dac se transmite un numr mare de parametrii,

numrul registrelor poate fi insuficient;


neeconomic sub aspectul timpului de execuie - necesit salvri i
restaurri frecvente de registre.

9.5.2. Prin variabile globale


n aceast metod, parametrii de intrare sunt transmii funciei prin
intermediul unor variabile ce pot fi accesate att de programul principal ct i de
funcie. Toate funciile au acces la locaiile de memorie definite n programul
principal. Deoarece aceste locaii sunt accesibile tuturor funciilor, ele se numesc
variabile globale. Funciile pot folosi variabilele globale n orice scop, incluznd
transferul datelor ntre program i funcii.
;
;memTr.asm
;
section .bss
mem resd 1
rez resd 1
section .text
functie:
;
not dword [mem]
neg dword [mem]
mov edx,[mem]
ret
global _start
_start:
nop
mov dword [mem],3
call functie
mov dword [mem],5
call functie
mov eax,1
mov ebx,0
int 080h
Programul memTr.asm demonstreaz cum pot fi interschimbate date ntre
funcie i programul principal prin intermediul variabilei globale mem.
Transmiterea parametrilor prin variabile globale presupune rezervarea de
memorie pentru fiecare din acetia, aadar poate fi neeconomic din punct de

vedere al spaiului de memorie ocupat (n special dac se folosesc structuri mari de


date necesare numai temporar).

9.5.3. Prin stiv


Stiva este accesibil programului principal, dar i tuturor funciilor folosite
n acel program, permind schimbul facil de date ntre programul apelant i
funcii. Prin folosirea stivei ca mijloc de tranfer al parametrilor sunt evitate
problemele ridicate de transferul datelor prin registre sau memorie. Aceasta este i
modalitatea de transfer a parametrilor preferat de toate funciile limbajului C. Din
acest motiv, metoda se mai numete apelul funciei n stilul C (C style function
calling).
Dac este folosit stiva, metoda obinuit de returnare a rezultatelor n
programul apelant se face prin:
registrul EAX pentru rezultate de 32 de bii;
alturarea registrelor EDX:EAX pentru rezultate de 64 de bii;
FPU ST(0) pentru valori n virgul mobil (floating point).
Ne amintim c stiva este alctuit din locaii de memorie rezervate la
sfritul spaiului de memorie alocat programului. Datele pot fi introduse sau
extrase numai de la vrful stivei. Vrful stivei este indicat de registrul ESP.
n mod obinuit, datele sunt introduse n stiv cu instruciunea PUSH i
extrase cu instruciunea POP. PUSH plaseaz elementul de date la adresa indicat
de registrul ESP i totodat decrementeaz automat valoarea acestuia astfel nct s
indice urmtoarea locaie liber. POP extrage din stiv elementul de date (ntr-un
registru sau locaie de memorie) i incrementeaz valoarea registrului ESP astfel
nct acesta s indice valoarea precedent.
Rulai urmtorul program.
;
;cStyleCall.asm
;
section .bss
rez1 resd 1
rez2 resd 1
section .text
global _start
_start:
nop

Afim valoarea registrului ESP:


(gdb) print /x $esp
$1 = 0xbfc65cd0
(gdb) n
9
call functie
(gdb) print /x $esp
$2 = 0xbfc65ccc
Afim coninutul stivei:
(gdb) x /1bd 0xbfc65ccc

push 5
call functie
mov [rez1],eax
push 10
call functie
mov [rez2],eax
mov eax,1
mov ebx,0
int 080h
functie:
pop eax
neg eax
ret

0xbfc65ccc: 5
(gdb) n
20
pop eax
(gdb) print /x $esp
$3 = 0xbfc65cc8
(gdb) x /xw 0xbfc65cc8
0xbfc65cc8: 0x0804808b
(gdb) x /2xw 0xbfc65cc8
0xbfc65cc8: 0x0804808b
0x00000005
(gdb) n
21
neg eax
(gdb) print /x $esp
$4 = 0xbfc65ccc
(gdb) print /x $eax
$5 = 0x804808b
(gdb) n
23
ret
(gdb) print /x $eax
$6 = 0xf7fb7f75
(gdb) n
0x00000005 in ?? ()

Teoretic, programul cSyleCall.asm ar fi trebuit s funcioneze corect.


Programul introduce paramentrul 5 n stiv i apeleaz funcia. Funcia extrage
paramentrul din stiv, i calculeaz complementul fa de doi i returneaz
rezultatul n registrul EAX. Dac nu ai fcut-o deja, rulai programul pas cu pas,
privind n stiv. ntr-adevr, programul principal introduce numrul 5 n stiv (de
unde ar trebui preluat de funcie cu ajutorul instruciunii POP). Dar am uitat c la
apelare, instruciunea CALL introduce automat n stiv adresa de revenire n
programul apelant. Aadar, ceea ce extrage POP-ul funciei este adresa de revenire,
creia i calculeaz complementul fa de doi. Pentru c adresa a fost extras din
stiv, instruciunea RET furnizeaz programului ca adres de revenire valoarea 5.
Programul nu mai poate continua. Am pierdut valoarea adresei de revenire.
Aspectul stivei la apelul funciei poate fi reprezentat astfel:

Stiv

Paramentrul funciei
ESP

Adresa de revenire
31

Recapitulm paii care apar n cazul furnizrii parametrilor funciei prin


intermediul stivei.
1. nainte de apelul funciei (prin instruciunea CALL), programul apelant
plaseaz la vrful stivei paramentrii de intrare necesari acesteia. Dac
funcia trebuie s primeasc mai muli parametrii, standardul C stabilete
ca acetia s fie introdui n stiv n ordinea invers extragerii lor de ctre
funcie (lucru normal din moment ce nu putem extrage din stiv dect n
ordinea invers introducerii).
2. Cnd este executat, instruciunea CALL plaseaz adresa de revenire n
programul apelant tot la vrful stivei, astfel nct funcia s tie de unde s
redea controlul acestuia.
n urma acestor pai, reprezentarea stivei arat astfel:
Stiv

Paramentru 3 funcie
Parametru 2 funcie
Paramentru 1 funcie
Adresa de revenire
31

ESP
0

Registrul ESP indic locaia adresei de revenire n program. Toi parametrii


de intrare ai funciei se gsesc deasupra adresei de revenire n program. Dac

extragem parametrii de intrare cu instruciunea POP, adresa de revenire s-ar putea


pierde (aa cum s-a ntamplat n programul nostru anterior). Aadar trebuie s
recurgem la o metod diferit de extragere a parametrilor din stiv.
ntr-un capitol anterior am prezentat adresarea bazat a memoriei.
Adresarea bazat pune la dispoziie o metod de acces la locaii de memorie pe
baza unei adrese aflate ntr-un registru. Deoarece ESP indic vrful stivei, pentru a
accesa parametrii de intrare fr a fi nevoie de extragerea lor cu POP, funcia poate
folosi adresarea bazat cu registrul ESP drept registru de baz. Fiecare paramentru
de intrare poate fi accesat indirect printr-un deplasament adunat la adresa din
registrul ESP (Atenie, n figur, fiecare csu reprezint 4 octei).
Stiv

Paramentru 3 funcie

ESP + 12

Parametru 2 funcie

ESP + 8

Paramentru 1 funcie

ESP + 4

Adresa de revenire

ESP

31

Lund n considerare modul n care arat stiva, putem schimba funcia


programului nostru astfel:
functie:
mov eax,[esp+4]
neg eax
ret
Rulm programul i vedem c de aceast dat adresa de revenire este
pstrat i totul funcioneaz corect. Totui, nu am scpat complet de neplceri.
Problem I:
Este posibil ca, n timpul rulrii funciei, aceasta s introduc date n stiv. Dac se
ntmpl acest lucru, valoarea lui ESP se modific i adresarea bazat nu d
rezultatul scontat. Presupunem c funcia trebuie s introduc n stiv coninutul
registrului EBX. Se ruleaz programul schimbnd secvena de cod a funciei astfel:

functie:
push ebx
mov eax,[esp+4]
neg eax
pop ebx
ret
Prin introducerea lui EBX n stiv, ESP + 4 indic adresa de revenire. Iari
extragem adresa de revenire i i calculm complementul fa de doi. De aceast
dat ns, din moment ce nu am extras-o cu POP, ci numai am adresat-o indirect,
nu pierdem valoarea adresei de revenire n program. ns rezultatul funciei este
eronat - am calculat complementul fa de doi al adresei de revenire, nu al
parametrului de intrare.
Rezolvare I:
Prima instruciune a funciei trebuie s salveze vrful iniial al stivei. Se copiaz
astfel valoarea registrului ESP n registrul EBP. Acest mecanism garanteaz c
exist ntotdeauna un registru care indic corect vrful iniial al stivei (existent la
momentul apelrii funciei). Datele introduse de funcie nu afecteaz EBP, doar
ESP.
functie:
mov ebp,esp
push ebx
mov eax,[ebp+4]
neg eax
pop ebx
mov esp,ebp
ret
Problem II:
Registrul EBP poate fi folosit de programul principal. Salvarea adresei de nceput a
stivei n EBP i suprascrie acestuia valoarea anterioar i poate deregla execuia
corect a programului apelant.
Rezolvare II:
nainte de copierea valorii din ESP n EBP, salvm registrul EBP n stiv. De
acum, codul de nceput i sfrit al funciei seamn cu un prolog, respectiv un

epilog.
functie:
push ebp
mov ebp,esp

prolog

<corpul funciei>
mov esp,ebp
pop ebp

epilog

ret
Stiv

EBP + 16

Paramentru 3 funcie
Parametru 2 funcie

EBP + 12

Paramentru 1 funcie

EBP + 8

Adresa de revenire

EBP + 4

ESP

Vechiul EBP
31

EBP
0

Datele locale funciei


Ultima reprezentare a stivei rezist pn cnd funcia nsi are nevoie s
foloseasc stiva pentru stocarea variabilelor locale (variabilele din interiorul
funciei).
Funcia ar putea folosi registre pentru stocarea propriilor variabile, dar
acestea pun la dispoziie un spaiu limitat de manevr, sau ar putea folosi variabile
globale, ceea ce ar nsemna s le crem noi n programul principal (s punem la
dispoziie elemente de date dedicate funciei). Aadar, tot stiva rmne cea mai
bun soluie.
S ne amintim: o dat ce am fixat EBP la vrful stivei, orice variabil
folosit n funcie (variabil local) poate fi plasat n stiv dup acel punct fr s
afecteze modul de acces la paramentrii de intrare. Ele pot fi adresate uor prin

intermediul registrul EBP. De exemplu, presupunnd valori de 4 octei, prima


variabil local poate fi adresat cu [EBP - 4], a doua cu [EBP - 8], etc..Funcia
care calculeaz complementul fa de doi i adun doi, devine:
functie:
push ebp
mov ebp,esp

prolog

push ebx
mov eax,[ebp+4]
mov [ebp-4],2
;variabil local 1
mov [ebp-8],6
;variabil local 2
mov [ebp-12],7 ;variabil local 3
neg eax
add eax,[ebp-4]
pop ebx
mov esp,ebp
pop ebp

epilog

ret
Stiv

Paramentru 3 funcie

EBP + 16

Parametru 2 funcie

EBP + 12

Paramentru 1 funcie

EBP + 8

Adresa de revenire

EBP + 4

Vechiul EBP

ESP

EBP

Variabil local 1

EBP - 4

Variabil local 2

EBP - 8

Variabil local 3
31

EBP - 12
0

Problem III:
Aa cum reiese din ultima figur, ESP nc indic vechea adres a EBP. Dac
funcia plaseaz n stiv variabile locale (ca n exemplul anterior Variabila local
1,2,3), dar n corpul ei mai conine i instruciuni PUSH, acestea, folosind ESP,
suprascriu valorile variabilelor locale. Pierdem variabilele funciei.
functie:
push ebp
mov ebp,esp
push ebx
mov eax,[ebp+4]
mov [ebp-4],2
mov [ebp-8],6
mov [ebp-12],7
push 20
push 60
push 70
neg eax
add eax,[ebp-4]
pop ebx
mov esp,ebp
pop ebp

Prolog (intrarea n funcie)

;variabil local 1
;variabil local 2
;variabil local 3
Suprascriu variabilele locale

Epilog (ieirea din funcie)

ret
Rezolvare III:
La nceputul funciei, imediat dup secvena de intrare n funcie (dup prolog),
rezervm n stiv un anumit spaiu pentru variabilele locale, scznd din ESP
valoarea dorit. Astfel, dac vor fi introduse n stiv date suplimentare, acestea vor
fi plasate sub variabilele locale. n aceste condiii, registrul ESP poate fi folosit n
mod obinuit, cu instruciunea PUSH, fr s afecteze variabilele funciei.
La sfritul funciei, registrul ESP este rescris cu valoarea sa de nceput
(n epilog) i variabilele funciei vor fi pierdute din stiv de aici i denumirea de
variabile locale.
Funcia programului nostru devine:

functie:
push ebp
prolog
mov ebp,esp
sub esp,12 ;rezerv 12 octei pentru variabilele locale
push ebx
mov eax,[ebp+4]
mov [ebp-4],2
;variabila local 1
mov [ebp-8],6
;variabila local 2
mov [ebp-12],7 ;variabila local 3
push 20
push 60
variabilele locale nu sunt suprascrise
push 70
neg eax
add eax,[ebp-4]
pop ebx
mov esp,ebp
pop ebp
ret

epilog

9.5.4. Structura funciei


Structura de ansamblu a funciei, cu noul prolog, este:
functie:
push ebp
mov ebp,esp
sub esp,12

Prolog (intrarea n funcie)

Corpul funciei
mov esp,ebp
pop ebp
ret

Epilog (ieirea din funcie)

Aspectul final al stivei este artat n Figura 9.2. Informaia stocat n stiv
parametrii de intrare, adresa de revenire n programul principal, vechea adres
EBP i variabilele locale formeaz ceea ce poart numele de cadru de stiv (stack

frame). Valoarea curent a registrului EBP se numete indicator de cadru (frame


pointer). Odat cunoscut, toate elementele cadrului de stiv pot fi accesate prin
intermediul ei.
Stiv

Paramentru 3 funcie

EBP + 16

Parametru 2 funcie

EBP + 12

Paramentru 1 funcie

EBP + 8

Adresa de revenire

EBP + 4

Vechiul EBP
-12
ESP

EBP

Variabil local 1

EBP - 4

Variabil local 2

EBP - 8

Variabil local 3

EBP - 12

31

0
Figura 9.2 Aspectul stivei

Procesoarele Pentium au introdus dou instruciuni care faciliteaz alocarea i


eliberarea cadrelor de stiv. Instruciunea ENTER se utilizeaz pentru alocarea
cadrului de stiv la intrarea n funcie. Formatul este
enter octei,nivel
unde octei specific numrul de octei dorit pentru stocarea variabilelor locale,
iar nivel precizeaz nivelul de imbricare al funciei. Dac specificm un nivel
diferit de zero, instruciunea copiaz n noul cadru de stiv nivel indicatori de
cadru, plecnd de la nceputul cadrului de stiv precedent. Asfel, instruciunea
enter <octei>, 0
este echivalent cu secvena de instruciuni
push ebp
mov ebp,esp

sub esp, <octei>


Instruciunea LEAVE elibereaz cadrul de stiv alocat cu instruciunea ENTER. Nu
primete niciun operand. Instruciunea LEAVE se traduce prin
mov esp,ebp
pop ebp
Instruciunea LEAVE trebuie s apar ntotdeauna naintea lui instruciunii RET.
Eliberarea stivei
Am vzut c programul principal plaseaz parametrii de intrare ai funciei
n stiv nainte de apelul acesteia. Cnd se iese din funcie, aceti parametrii sunt
tot n stiv, deoarece funcia i-a accesat prin adresarea indirect, nu prin
instruciunea POP.
Stiv

Parametru 3 funcie
Paramentru 2 funcie
Parametru 1 funcie
31

ESP
0

POP extrage din stiv, elementul extras cu POP nu mai rmne n stiv.
Adresarea bazat l copiaz, elementul rmne n stiv.
Totui, dac programul principal folosete stiva i n alte scopuri este de
preferat ca acesta s regseasc stiva exact n stadiul n care se afla nainte de
apelul funciei (eliberat de parametrii de intrare ai unei funcii care s-a sfrit).
Procesul de eliberare a stivei de parametrii nedorii poate fi realizat de
ctre:
funcia apelat;
programul apelant (programul principal).
Dac funciile primesc un numr fix de parametri, se prefer prima metod.

n acest caz, codul de ndeprtare a parametrilor este scris o singur dat, n corpul
funciei, chiar dac funcia este apelat de mai multe ori. Avei n vedere c nu
putei folosi o metod de genul
functie:
<corpul_funciei>
leave
add esp,12
ret
deoarece la execuia lui RET, ESP trebuie s indice ctre adresa de revenire n
programul principal. Soluia este reprezentat de operandul opional care poate fi
specificat imediat dup RET.
ret operand
care rezult n urmtoarea secven de instruciuni:
EIP SS:ESP
ESP ESP + 4 + operand
Operandul trebuie s fie un imediat de 16 bii. Din moment ce scopul acestei valori
opionale este s descarce parametrii introdui n stiv, operandul are ntotdeauna o
valoare pozitiv.
Totui, dac o funcie primete un numr variabil de parametri, trebuie s
folosim a doua metod. Aceasta este i metoda utilizat de compilatoarele C. Din
programul principal, parametrii de intrare pot fi extrai cu instruciunea POP, dar
cel mai indicat este s reiniializm registrul ESP cu adresa anterioar apelului
funciei. Pentru aceasta se adun la ESP mrimea parametrilor de intrare introdui
n stiv. De exemplu, dac introducem n stiv, ca parametri de intrare pentru o
funcie, trei ntregi a cte 4 octei fiecare, pentru ndeprtarea acestora din stiv, la
sfritul textului funciei din programul principal, imediat dup instruciunea de
apel, trebuie s adunm valoarea din ESP cu 12.
Tabelul 9.1 Exemple de eliberare a stivei din interiorul programului principal

push eax
push ebx
push ecx
call functie
add esp,12

push eax
push ebx
call functie
add esp,8

push eax
call functie
add esp,4

9.6. Mecanisme de transfer al parametrilor


Partea cea mai important din procesul de apelare a funciilor este
reprezentat de transferul parametrilor la apel. Exist dou mecanisme de transfer
al parametrilor:
transfer prin valoare (call-by-value)
transfer prin referin (call-by-reference).

9.6.1. Transfer prin valoare


Mecanismul de transfer al parametrilor prin valoare furnizeaz funciei
apelate numai valoarea curent a parametrului de care are nevoie. Din aceast
cauz, programatorul trebuie s aloce dou locaii de memorie: una accesibil
programului definit n segmentul de date - pe care funcia nu o poate modifica,
i una accesibil funciei definit pe un nivel de stiv pe care funcia o poate
modifica. n acest caz, spunem c transmitem acel parametru prin valoare, i
valoarea nu poate fi modificat de funcia apelat.
Urmtorul program exemplific transferul parametrilor prin valoare.
Funcia nmulete dou valori primite prin stiv i tot ea elibereaz stiva la final.
Rezultatul este returnat conform standardului de apel C, n perechea de registre
EDX:EAX (fiind valoare de 64 de bii).
;
;callByValue.asm
;
section .data
val1
dd
150
val2
dd
10
section .text
global _start
_start:
nop
push dword [val1]
push dword [val2]
call multiply
mov eax,1
mov ebx,0
int 80h
multiply:
enter 0,0
mov eax,[ebp+8]

mul dword [ebp+12]


leave
ret 8
Rulm programul prin intermediul depanatorului. Imediat dup comanda de rulare
afim coninutul registrului ESP (vrful stivei). Apoi introducem valorile n stiv.
(gdb) i r esp
esp
0xffffd7d0
0xffffd7d0
(gdb) n
13
push dword [val2]
(gdb)
14
call multiply
(gdb) i r esp
esp
0xffffd7c8
0xffffd7c8
Dac scdei adresa curent a ESP din adresa iniial rezult c au fost introdui n
stiv 8 octei. Nu intrai nc n funcie. Studiai coninutul cadrului de stiv curent
cu ajutorul comenzilor backtrace i info frame. Acesta este cadrul de stiv
al programului principal.
Intrai n funcie cu instruciunea nexti.
(gdb) ni
0x080480a2 in multiply () at sum.asm:19
19
enter 0,0
Instruciunea ENTER a fost deja executat. Putei afia cadrele de stiv prezente i
detalia cadrul de stiv al funciei.
(gdb) bt
#0 0x080480a2 in multiply () at sum.asm:19
#1 0x08048092 in _start () at sum.asm:14
(gdb) info frame
Stack level 0, frame at 0xffffd7c8:
eip = 0x80480a2 in multiply (sum.asm:19); saved eip 0x8048092
called by frame at 0xffffd7cc
source language unknown.
Arglist at 0xffffd7c0, args:
Locals at 0xffffd7c0, Previous frame's sp is 0xffffd7c8
Saved registers:
eip at 0xffffd7c4

(gdb) i r ebp
ebp
0xffffd7c0
(gdb) i r esp
esp
0xffffd7c0

0xffffd7c0
0xffffd7c0

nainte de intrarea n funcie, adresa registrului ESP era 0xffffd7c8. Dup


parcurgerea prologului, adresa din registrul ESP este 0xffffd7c0. O diferen de 8
octei 4 octei ocupai cu adresa de revenire n program (introdus automat de
instruciunea CALL) i 4 octei ocupai de valoarea registrului EBP, salvat n stiv
n cadrul prologului (PUSH EBP). Registrul EBP are aceeai valoare cu registrul
ESP deoarece a fost executat instruciunea MOV EBP,ESP. Aadar, indicatorul
de cadru EBP are valoarea 0xffffd7c0. Din acest motiv, n descrierea cadrului de
stiv se specific faptul c lista argumentelor i a variabilelor locale ncepe de la
aceast adres.
(gdb) x /4wx $ebp
0xffffd7c0: 0x00000000

0x08048092

0x0000000a

0x00000096

Am afiat patru cuvinte de 32 de bii ncepnd de la adresa EBP. Prima valoare din
stnga, 0x00000000, reprezint adresa anterioar a registrului EBP, a doua,
0x08048092, reprezint adresa de revenire n programul apelant (adresa care va fi
ncrcat n registrul EIP de instruciunea RET), a treia i a patra valoare sunt
datele de intrare n funcie - 10, respectiv 150, n hexazecimal.
Urmtoarele dou instruciuni efectueaz nmulirea. Prima copiaz n
registrul EAX un parametru din stiv prin adresare bazat. Rezultatul rmne n
combinaia de registre EDX:EAX. Deoarece valoarea este mic, registrul EDX
rmne 0.
(gdb) si
0x080480a5
20
(gdb) i r eax
eax
0xa
10
(gdb) si
0x080480a8
21
(gdb) i r eax
eax
0x5dc 1500
(gdb) i r edx
edx
0x0
0

mov eax,[ebp+8]

mul dword [ebp+12]

La ieirea din funcie, instruciunea RET repoziioneaz indicatorul de stiv la


adresa acestuia dinaintea introducerii parametrilor.

(gdb) i r esp
esp
0xffffd7d0

0xffffd7d0

9.6.2. Transfer prin referin


Mecanismul de transfer al parametrilor prin referin furnizeaz funciei
apelate adresa parametrului de care are nevoie. n acest caz se folosete o singur
locaie de memorie, dar zona de memorie respectiv trebuie s fie vizibil funciei
apelate. Funcia poate modifica coninutul parametrului i aceste modificri sunt
vizibile din programul apelant prin manipulearea direct a valorii sale. n acest
caz, spunem c transmitem acel parametru prin adres.
Urmtorul program exemplific transferul parametrilor prin adres.
Deoarece este echivalentul programului anterior, n continuare ne mrginim s
specificm numai diferenele.
;
;callByRef.asm
;
section .data
val1
dd
150
val2
dd
10
section .text
global _start
_start:
nop
push dword val1
push dword val2
call multiply
mov eax,1
mov ebx,0
int 80h
multiply:
enter 0,0
push ebx
mov ebx,[ebp+8]
mov eax,[ebx]
mov ebx,[ebp+12]
mul dword [ebx]
pop ebx
leave
ret 8

Adresa iniial a indicatorului de stiv este tot 0xffffd7d0.


(gdb) i r esp
esp
0xffffd7d0
0xffffd7d0
(gdb) n
9
push dword val2
(gdb)
10
call multiply
(gdb) x /2wx $esp
0xffffd7c8: 0x080490b4
0x080490b0
Observai c n stiv sunt introduse adresele parametrilor, nu valorile acestora.
Valorile sunt gsite prin intermediul acestor adrese.
(gdb) x /1wd 0x080490b4
0x80490b4 <val2>:
10
(gdb) x /1wd 0x080490b0
0x80490b0 <val1>:
150
Dup executarea instruciunii ENTER, indicatorul de cadru poate reprezenta adres
de baz pentru
(gdb) x /4wx $ebp
0xffffd7c0: 0x00000000

0x08048090

0x080490b4

0x080490b0

vechea valoare a registrului EBP, adresa de revenire n program, i adresele


parametrilor (nu valorile parametrilor ca n programul precedent).
(gdb) si
0x080480a4
17
mov ebx,[ebp+8]
(gdb) i r ebx
ebx
0x80490b4
134516916
(gdb) i r eax
eax
0x0
0
(gdb) si
18
mov eax,[ebx]
(gdb) i r eax
eax
0xa
10
Valoarea celui de al doilea parametru este ncrcat n registrul EAX prin adresare

indirect cu registrul EBX. Cellalt parametru se acceseaz tot prin adresare


indirect.
De aici pn la final, comportamentul programului este cunoscut.

9.7. Conservarea strii registrelor


Totui, n funcie se petrece un lucru foarte important. nainte de utilizarea
registrului EBX n adresarea indirect, instruciunea
push ebx
salveaz coninutul acestuia n stiv. Exist posibilitatea ca registrul EBX s fie
folosit de programul apelant i funcia se asigur c operaiile sale nu modific
coninutul acestuia. Conservarea strii registrelor de-a lungul execuiei unei funcii
este un concept foarte important. Funcia apelat trebuie s salveze toate registrele
pe care le folosete i s le refac nainte de a reda controlul programului apelant.
Acesta este un principiu important al programrii modulare.
Pentru a evita selecia individual a registrelor ce trebuie salvate, am putea
salva toate registrele la nceputul funciei apelate i le-am putea restaura la sfrit.
De exemplu, am putea utiliza instruciunile PUSHAD i POPAD. Instruciunea
PUSHAD este util n anumite situaii, dar nu n toate.
n primul rnd, unele registre salvate de PUSHAD sunt folosite la returnarea
valorilor de ieire (EAX pentru rezultate ntregi de 32 de bii, sau EDX - n cazul
unor rezultate ntregi de 64 de bii). Instruciunea POPAD distruge rezultatele
returnate de funcia apelat.
n al doilea rnd, PUSHAD consum cinci cicluri de tact, n timp ce o
singur instruciune PUSH consum numai unul. Instruciunea PUSHAD devine
eficient numai dac sunt salvate mai mult de cinci registre.
n plus, nu uitai c o instruciune PUSHAD modific semnificativ
deplasamentul la care pot fi gsii n stiv parametrii de intrare ai funciilor apelate.

9.8. Scrierea funciilor n fiiere separate


Scrierea funciilor conform standardului C (folosirea stivei) mai prezint
un avantaj: funcia i este suficient siei, n sensul c nu este nevoie s definim
locaii globale de memorie pentru accesul la date (variabile globale). Altfel spus,
nu avem nevoie de directive section .data sau .bss n funcii (vezi

variabile locale). Acest fapt aduce la rndul su i alt beneficiu: nu este imperios
necesar ca funcia s se afle n acelai fiier cu programul principal. n continuare
artm cum putem crea fiiere separate pentru funcii, cum trebuie s le asamblm
i cum trebuie s le legm de fiierul programului principal.
Structura funciei scrise ntr-un fiier separat este similar structurii unui
program obinuit, numai c, n loc de directiva _start, trebuie s declarm ca
etichet global numele funciei.
section .text
global _functie
_functie:
De asemenea, n fiierul programului principal trebuie s folosim directiva
extern <nume_funcie>.
Din perspectiva procesului de asamblare, fiecare fiier .asm separat este
considerat un modul, indiferent dac conine o etichet _start (caz n care este
considerat program principal) sau dac reprezint o simpl funcie. Fiecare modul
conine cod i, posibil, cteva definiii de date. Directiva extern indic
asamblorului c funcia respectiv se afl ntr-un alt modul. Directiva global
atenioneaz asamblorul c funcia respectiv poate fi adresat din exteriorul
modului. Convenia extern global se aplic i declaraiilor de date. Putem
declara orice etichet de date ca global i ea poate fi utilizat de orice modul n
care numele etichetei respective apare declarat ca extern. Mai mult, fiierele de
funcii pot partaja ntre ele date i funcii, n orice combinaie, att timp ct toate
declaraiile globale i externe sunt realizate corect. Un modul care conine funcii
sau date declarate global se spune c export acele elemente. Similar, un modul
care utilizeaz funcii sau date externe se spune c import acele elemente.
Modulele asamblate separat au structur similar cu programele normale, cu o
excepie important: modulele externe nu conin un program principal, deci nu au
adres de nceput. Acest lucru nseamn c nu exist o etichet _start care s
indice editorului de legturi punctul de la care trebuie s nceap execuia
programului. Modulele de funcii nu sunt destinate s ruleze singure, astfel nct
prezenta etichetei _start este inutil. Editorul de legturi va genera o eroare ori
de cte ori ntlnete mai mult de o etichet _start n modulele pe care le
proceseaz la un moment dat. Dac toate declaraiile sunt corecte, modulele pot
comunica unul cu altul prin intermediul apelurilor de funcie, i orice funcie poate
adresa orice definiie de date din oricare din fiierele legate de editorul de
legturi.
Scriem codul funciei care calculeaz complementul fa de doi ntr-un
fiier separat de cel al programului principal. Vor rezulta dou fiiere:
functie.asm, pentru corpul funciei, i main.asm, pentru programul principal.

;
;functie.asm
;
section .bss
mem resd 1
section .text
global _functie
_functie:
push ebp
mov ebp,esp
sub esp,8
mov eax,[ebp+8]
neg eax
leave
ret

;
;main.asm
;
section .bss
rez1 resd 1
rez2 resd 1
section .text
extern _functie
global _start
_start:
nop
push 5
call _functie
add esp,4
mov [rez1],eax
push 10
call _functie
add esp,4
mov [rez2],eax
mov eax,1
mov ebx,0
int 080h

Nu exist o limit cu privire la numrul de directive extern prezent ntrun modul. Un modul (program sau funcie) poate primi oricte directive extern,
corespunztor numrului de funcii externe apelate. Pentru un aspect compact,
funciile pot fi declarate i pe o singur linie, separate prin virgul.
extern f1, f2, f3
Totui, declaraiile care trec de limita liniei nu sunt recunoscute.
Directivele globale trebuie declarate naintea definirii lor n codul surs. n
practic, acest lucru nseamn c directivele global apar n codul surs la
nceputul seciunii TEXT, naintea oricrei funcii. Similar, toate elementele de date
globale sunt declarate ca atare n segmentul DATA naintea definirii lor. Elementele
care nu sunt declarate global sunt private, n sensul c nu pot fi accesate dect
din interiorul modulului care le conine.
Asamblm cu urmtoarele comenzi:

yasm -f elf -g stabs


yasm -f elf -g stabs
ld -o program main.o
gdb program

main.asm
functie.asm
functie.o melf_i386

Ordinea modulele n linia editorului de legturi este foarte important.


Vom discuta acest fapt ntr-un capitol viitor.
Reasamblm programul folosind modulul functie2.asm. Vedem c
funcia folosete stiva, dar totul funcioneaz corect din moment ce am rezervat un
spaiu de 8 octei cu instruciunea SUB ESP,8.
;
;funcie2.asm
;
section .bss
mem resd 1
;variabila mem se gsete n memorie la adresa imediat urmtoare variabilelor
;rez1 i rez2 definite n fiierul programului principal
section .text
global _functie
_functie:
push ebp
mov ebp,esp
sub esp,8
mov eax,[ebp+8]
mov [mem],eax
add eax,1
push eax
neg dword [mem]
mov eax,[mem]
pop eax
leave
ret
Presupunem c funcia folosete variabile locale, adresate cu [ebp-4], [ebp-8]:
;
;functie3.asm
;
section .bss
mem resd 1

section .text
global _functie
_functie:
push ebp
mov ebp,esp
sub esp,8
mov eax,[ebp+8]
mov [mem],eax
mov eax,0ffffffffh
mov ebx,0aaaaaaaah
mov ecx,022222222h
push eax
push ebx
mov [ebp-4],ecx
mov dword [ebp-4],033333333h
neg dword [mem]
mov eax,[mem]
pop eax
leave
ret
Rulai programul pas cu pas i observai cum se modific coninutul stivei.
Poate ai observat c n aceste ultime funcii nu am folosit instruciunea
ENTER, ci am preferat secvena clasic de intrare n funcie. Majoritatea
compilatoarelor au aceeai preferin. Acest lucru se explic prin faptul c
instruciunea ENTER are probleme de performan. Procesoarele moderne
decodeaz ENTER n 10 pn la 20 de microoperaii, n timp ce secvena de trei
instruciuni este decodat n 4 pn la 6, n funcie de arhitectur. Diferena de
vitez este destul de mare. n plus, secvena de trei instruciuni poate fi optimizat
de asamblor.

9.9. Exerciii
9.1. Scriei urmtoarea funcie C:
/* f.c */
int f(void) {
return 0;

}
Afiai echivalentul ei n asamblare cu comanda:
gcc -S f.c -o - -m32 -masm=intel

-O0

Comparai rezultatul cu cel al urmtoarelor dou funcii:


int g(void) {
}

int h(void) {
return 56;
}

9.2. Scriei urmtorul program C:


#include <stdio.h>
int main(){
int x = 1;
double y,z;
y = 1.23;
z = (double)x +y;
return 0;
}
Folosii comanda prezentat la exerciiul anterior pentru afiarea echivalentului su
n limbaj de asamblare. Identificai secvena de instruciuni n limbaj de asamblare
care realizeaz adunarea z = (double)x +y. Descriei operaiile efectuate.
9.3. Scriei n fiiere separate urmtoarele dou programe i studiai echivalentul
acestora n limbaj de asamblare.
#include <stdio.h>
void swap( int num1, int num2 ) ;

#include <stdio.h>
void swap( int *num1, int *num2 ) ;

int a = 256;
int b = 128;

int a = 256;
int b = 128;

void main( )
{
swap( a, b ) ;

void main( )
{
swap( &a, &b ) ;

void swap( int num1 , int num2 )


{
int temp ;

void swap( int *num1 , int *num2 )


{
int temp ;

temp = num2 ;
num2 = num1 ;
num1 = temp ;
}

temp = *num2 ;
*num2 = *num1 ;
*num1 = temp ;
}

Generai executabilele cu comanda:


gcc -g f.c -o - -m32 -masm=intel

-O0 -o f

i depanaile cu GDB. Care sunt valorile variabilelor a i b la sfritul celor dou


programe? Descriei operaiile care au loc n fiecare caz n parte.

10. INTERFAA CU SISTEMUL DE


OPERARE

Odat lansat n execuie, niciun program ntlnit n capitolele precedente


nu interacioneaz n vreun fel cu utilizatorul. Programele interactive, care
accept date de la tastatur sau afieaz informaii la monitor, folosesc servicii
puse la dispoziie de sistemul de operare. tim deja c sistemul de operare
gestioneaz i controleaz de o maniera strict toate elementele hardware ale
mainii de calcul: discurile, imprimanta, tastatura, diferite porturi (USB, Ethernet,
etc..), monitorul. Accesul unui program la aceste componente se face numai prin
intermediul funciilor puse la dispoziie de sistemul de operare. Modul prin care
atragem atenia sistemului de operare c avem nevoie de suportul su n realizarea
unei operaii se realizeaz prin apeluri de sistem (o categorie special de
ntreruperi software). n acest capitol studiem toate aspectele legate de modul n
care sistemul de operare ne permite s interacionm cu sistemul de calcul.

10.1. ntreruperi software


Noiunea de ntrerupere o lum ca atare, n sensul de a suspenda temporar
cursul, desfurarea unei aciuni, a unei activiti. n cazul de fa, cel care
efectueaz activitatea (de ex., rularea unui program) este procesorul, iar
ntreruperea poate fi declanat de o component hardware care are nevoie de
atenia sa. n acest caz, procesorul suspend procesul curent i ruleaz secvena de
cod (rutina) specific acelei componente. De exemplu, dac semnalul de
ntrerupere provine de la controlerul portului serial, procesorul permite acestuia s
transfere un caracter. Deoarece ntreruperea a fost iniiat de o component
hardware, se numete ntrerupere hardware, iar secvena de instruciuni main
centrat pe o anumit sarcin poart numele de rutin de tratare a ntreruperii (ISR
Interrupt Service Routine). Fiecare secven de acest gen, specific fiecrui
sistem de operare, realizeaz ceva util citete un fiier, scrie un fiier, extrage
timpul curent, citete un port de reea, .a.m.d..
Sistemul de operare folosete secvenele de ntrerupere n scop propriu, dar
le poate pune i la dispoziia programatorului. ns programatorul nu le poate apela
direct, i asta din dou motive ntemeiate. n primul rnd, accesul nerestricionat al
programelor din spaiul utilizator la componentele intime ale sistemului de operare

este periculos. Persoane ru intenionate le-ar putea modifica sau folosi n scopuri
improprii. n al doilea rnd, aceste secvene se modific odat cu mbuntirea i
evoluia sistemului de operare. Evoluia presupune adugarea, modificarea sau
tergerea unor instruciuni main din motive de securitate sau de optimizare.
Din aceste motive, apelul funciilor de sistem se face prin ceea ce n limbaj
de specialitate se numete poart de apel (call gate) i anume, o poart de acces
aflat ntre spaiul utilizator, acolo unde ruleaz programele obinuite, i spaiul
kernel, acolo unde i desfoar activitatea componenta sistemului de operare care
gestioneaz resursele hardware ale sistemului. Poarta de apel este implementat
printr-o ntrerupere software.
n sistemele x86, chiar la nceputul memoriei RAM, la adresa 0, se afl un
tabel special format din 256 de intrri. Fiecare intrare reprezint o adres de
memorie de 4 octei. Astfel, primii 1024 de octei de memorie sunt rezervai acestui
tabel. Fiecare intrare (adres) din tabel se numete vector de ntrerupere. Din acest
motiv, tabelul este denumit tabelul vectorilor de ntrerupere. n funcie de poziia
sa n tabel, fiecare vector are un numr de identificare cuprins ntre 0 i 255. La
iniializarea calculatorului, BIOS-ul i sistemul de operare20 ncarc locaiile din
tabel cu adresele rutinelor de ntrerupere specifice lor. O actualizare a sistemului de
operare poate modifica att rutinele ct i adresele prezente n tabelul vectorilor de
ntrerupere, n schimb numrul (indexul) ntreruperii care conine respectiva adres
rmne nemodificat. Cu alte cuvinte, rolul locaiilor din tabelul vectorilor de
ntrerupere este standard, secvena de ntrerupere i adresa ei n memorie se poate
modifica (de ex., adugarea unor noi instruciuni la o rutin de ntrerupere crete
dimensiunea acesteia i trebuie ncrcat la alt adres de memorie), dar
ntotdeauna va fi apelat de la vectorul cu acelai numr. Din aceast cauz, nc de
la prima apariie a sistemului Linux i pn n prezent, numrul de ntrerupere 80H
a indicat ntotdeauna ctre un dispecer de apel (servicii), un fel de manager al
tuturor funciilor de sistem puse la dispoziie de kernelul Linux. Adresa
dispecerului difer de la distribuie la distribuie sau chiar de la o versiune de Linux
la alta, dar indiferent de acest lucru, programele l pot apela prin intermediul
locaiei 80H din tabelul vectorilor de ntrerupere. Din motive de securitate, tabelul
vectorilor de ntrerupere aparine sistemului de operare i programatorul nu are
acces direct la adresele sale. Totui, programatorul dispune de o instruciune ce
poate interoga respectivul tabel - instruciunea INT (INTerrupt). Singura diferen
real ntre ntreruperile hardware i ntreruperile software const n evenimentul
care declaneaz saltul procesorului la tabela vectorilor de ntrerupere. n cazul
unei ntreruperi hardware, evenimentul declanator este un semnal electric aplicat
pe un pin al procesorului. Dup ce recunoate semnalul electric, procesorul
introduce n stiv adresa de revenire n program i execut rutina de tratare a
respectivei ntreruperi. Pentru ntreruperile software, evenimentul declanator este
20

De fapt, sistemele de operare folosesc o adres specific pentru vectorul de ntreruperi.

intruciunea INT. Cnd este executat instruciunea INT 80H, procesorul extrage
adresa gsit la locaia 80H a tabelei vectorilor de ntrerupere i execut secvena
de instruciuni indicat de acea adres. n acest mod se efectueaz i se controleaz
tranziia din spaiul utilizator n spaiul kernel. Procesul este ilustrat n Figura 10.1.
Prima dat, instruciunea
INT 80H introduce n stiv
adresa urmtoarei instruciuni

Stiva
Adresa de revenire

Codul programului

... apoi sare la adresa stocat


n vectorul 80h

INT 80h
(Urmtoarea instruciune)
SPAIUL UTILIZATOR
SPAIUL KERNEL

Linux

Dispecer de apel

Tabela vectorilor de
ntrerupere
Vector 80h

Figura 10.1 Apelul vectorului de ntrerupere 80h

Instruciunea INT 80H introduce n stiv adresa urmtoarei instruciuni din


program (adresa care urmeaz imediat instruciunii INT 80H) i apoi sare n spaiul
kernel la adresa vectorului 80H. De aici, dispecerul de apel controleaz accesul la
aproximativ 200 de rutine de sistem individuale. Ca s tie pe care din ele s o
execute dispecerul caut n registrul EAX un identificator de apel. nainte de apelul
dispecerului (cu INT 80H), programatorul trebuie s specifice n EAX un
identificator de apel. Pe lng acesta, programatorul trebuie s specifice i alte
informaii, furnizate aproape ntotdeauna tot prin intermediul unor registre. Dup
finalizarea secvenei de ntrerupere, instruciunea IRET folosete adresa salvat

anterior n stiv pentru a reveni n program. Acest proces seamn cu cel prin care
funciile studiate n capitolul precedent folosesc perechea de instruciuni CALL i
RET. CALL introducea n stiv adresa urmtoarei instruciuni i srea la funcie,
instruciunea RET de la sfritul funciei extrgea adresa din stiv i permitea
continuarea execuiei programului de la prima instruciune sub CALL. Este timpul
s nelegem rolul ultimelor trei instruciuni din toate programele studiate n
aceast carte pn n prezent.
;
;ntrerupe execuia programului i red controlul sistemului de operare
;
mov eax,1 ;specific funcia de sistem Exit
mov ebx,0 ;cod de ntoarcere zero (SUCCESS)
int 80h
;efectueaz apel de sistem n vederea ntreruperii
execuiei
Programatorul, pentru a ntrerupe execuia programului, trebuie s plaseze
identificatorul de apel sys_exit, 1, n registrul EAX i un cod de ntoarcere n EBX,
apoi specific instruciunea INT 80H. Codul de ntoarcere este o valoare numeric
aflat la discreia sa. Tehnic, nu sunt restricii (trebuie numai s ncap ntr-un
registru de 32 de bii) dar, prin convenie, o valoare de ntoarcere 0 nseamn c
operaia s-a finalizat cu succes (procesul a fost ntrerupt normal). O valoare diferit
de 0 indic o eroare (ntmpinat de kernel n procesul de ntrerupere): fiierul nu
poate fi gsit, discul este la capacitate maxim, etc..
Fiecare program trebuie ntrerupt n acest mod. Chiar dac un program nu
implementeaz funcia sys_exit, pn la urm va fi ntrerupt, dar sistemul de
operare va afia o eroare tip Segmentation fault i nu putem tii cum a fost
finalizat procesul de ntrerupere.

10.2. Formatul apelurilor de sistem


Utilizarea funciilor de sistem n programele scrise n limbaj de asamblare
poate fi complicat. Spre deosebire de funciile C, unde valorile de intrare erau
plasate n stiv, apelurile de sistem necesit ca valorile de intrare s se afle n
registre. Locul acestora n registre este prestabilit. Plasarea unei valori de intrare
ntr-un registru greit produce rezultate neateptate sau poate duce la ntreruperea
prematur a programului.
Deja ai aflat c registrul EAX trebuie s conin identificatorul de apel.
Cum registrele EIP, EBP i ESP nu pot memora valori de intrare, deoarece ar
afecta operarea normal a programului, rmn disponibile numai cinci registre. Aa

cum am menionat, ordinea n care valorile de intrare sunt plasate n registre este
foarte important. Dispecerul de apel ateapt datele de intrare n ordinea
urmtoare:
EBX, primul parametru;
ECX, al doilea parametru;
EDX, al treilea parametru;
ESI, al patrulea parametru;
EDI, al cincilea parametru.
Funciile de sistem care necesit mai mult de ase parametrii de intrare
folosesc o metod diferit de preluare a acestora: dispecerul adreseaz i citete
parametrii prin intermediul registrului EBX. n acest caz, registrul EBX conine
adresa unei locaii de memorie de la care parametrii sunt stocai n ordine
secvenial.
O alt problem const n stabilirea corect a numrului de parametrii i a
registrului corespunztor unui anumit parametru. Semnificaia parametrilor, la fel
ca i numrul acestora, difer de la funcie la funcie. Asemenea oricrei comenzi
Linux, odat ce ai aflat numele funciei de sistem poi gsi definiia acesteia n
paginile manual. Seciunea 2 a paginilor de manual conine definiiile tuturor
funciilor de sistem disponibile. Accesul la definiia unei funcii de sistem se face
cu ajutorul comenzii man urmat de numrul 2:
man 2 exit
Numrul 2 specific seciunea paginilor de manual. Deoarece unele funcii de
sistem au acelai nume cu unele comenzi listate n seciunea 1, implicit pentru
paginile de manual, nu uitai s includei 2 explicit. Altfel, n loc s obinei
definiia funciei de sistem, vor fi afiate opiuni de comand. Pagina de manual
conine patru pri:
Nume: arat numele funciei de sistem;
Rezumat: arat modul n care putem utiliza funcia de sistem
respectiv;
Descriere: o scurt descriere a funciei de sistem;
Valoarea returnat: valoarea returnat la sfritul funciei de sistem.
Rezumatul este scris pentru programatori de C, dar pot beneficia de aceste
informaii i cei care programeaz n limbaj de asamblare. Rezumatul funciei de
sistem EXIT arat c aceasta primete un singur parametru de intrare (valoarea
ntre paranteze) i nu returneaz nicio valoare.
Funcia de sistem WRITE este utilizat la scrierea datelor ntr-un descriptor
de fiier. Dac afim pagina sa de manual, rezumatul se prezint astfel:

SYNOPSIS
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
Parametrii sunt menionai de la stnga la dreapta, aa cum apar n rezumat.
Primul parametru (fd) este un ntreg care reprezint descriptorul de fiier pentru
dispozitivul de ieire, un identificator de fiier. Al doilea parametru (buf) este
adresa (pointer) de la care ncepe irul ce trebuie scris la dispozitiv. Al treilea
parametru (count) reprezint dimensiunea n octei a irului care trebuie scris.
Utiliznd aceast convenie, valorile de intrare sunt atribuite urmtoarelor registre:
EBX, descriptorul de fiier;
ECX, adresa de memorie a irului (pointer-ul);
EDX, numrul de octei care trebuie scris din memorie n fiier.
Un exemplu de utilizare a acestei funcii de sistem este dat n programul
hello.asm.
;
;afieaz la monitor mesajul Hello, World!
;
section .data
mesaj db Hello, World, 0xa
;0xA = ASCII newline
lungime
equ $ - mesaj
section .text
global _start
_start:
nop
;pentru a putea seta punctul de ntrerupere n depanator
mov eax,4 ;EAX = identificatorul funciei de sistem write
mov ebx,1 ;EBX = descriptorul de fiier 1 (STDOUT)
mov ecx,mesaj ;ECX = adresa de nceput a irului care va fi trimis la
STDOUT
mov edx,lungime ;EDX = numrul de octei trimii la STDOUT
int 80h
;specific kernelului s execute funcia indicat n EAX
;WRITE returneaz n EAX numrul de caractere scris la monitor
mov eax,1 ;EAX = identificatorul funciei de sistem EXIT
mov ebx,0 ;EBX = codul de ntoarcere din SO n caz de succes
int 80h
;specific kernelului s execute funcia indicat n EAX
Probabil v este cunoscut cam tot ce se ntmpl n programul hello.asm.
Identificatorul de apel pentru funcia de sistem WRITE (4) este plasat n registrul

EAX. Valorile de intrare necesare sunt plasate n registrele predefinite. ns pentru


a nelege ce reprezint valoarea plasat n registrul EBX trebuie s vorbim despre
fiiere standard n Linux.
Fiiere standard
Principiul fundamental de proiectare a sistemelor Unix i nrudite este
everything is a file. Sintagma orice este un fiier trebuie neleas ca: orice
este reprezentat ca fiier. Un fiier poate fi o colecie de date pe disc dar, ntr-un
sens mai general, un fiier este punctul final al unui traseu parcus de date. Cnd
scrii ntr-un fiier, trimii date de-a lungul unui traseu ntr-un punct final. Cnd
citeti un fiier, preiei date dintr-un punct final. n cazul unui transfer ntre fiiere,
calea parcurs de date se poate afla n totalitate n interiorul unui calculator sau
poate traversa o reea de calculatoare, datele pot suferi modificri de-a lungul
traseului sau nu, .a.m.d.. Ceea ce trebuie s nelegem este c, n sistemele Unix,
totul este reprezentat ca fiier i toate fiierele, indiferent de natura lor, sunt tratate
de ctre mecanismele interne ale sistemului de operare mai mult sau mai puin
identic. n acest sens, fiierele nu sunt reprezentate numai de coleciile de date
stocate pe disc, ci de toate componentele hardware care pot juca rol de surs sau
destinaie pentru date. Tastatura este un fiier: un punct final care genereaz date i
le trimite undeva. Monitorul este un fiier: un punct final care primete date de
undeva i le afieaz. Fiierele Unix nu sunt neaprat fiiere text. Fiierele binare
au parte de acelai tratament. Tabelul 2.5 prezint cele trei fiiere standard definite
de sistemele Unix sau nrudite, cum este Linux. Aceste fiiere sunt deschise i
disponibile ntotdeauna n timpul rulrii programelor.
Tabelul 10.1 Cele trei fiiere standard n Unix

Fiier
Standard Input
Standard Output
Standard Error

Identificator C
STDIN
STDOUT
STDERR

Descriptor de fiier
0
1
2

Hardware
Tastatur
Monitor
Monitor

Pentru sistemul de operare, un fiier este reprezentat printr-un descriptor


de fiier. Descriptorul de fiier este un numr ntreg folosit ca identificator pentru
acel fiier. Primele trei numere aparin celor trei fiiere standard. Dac un program
deschide un fiier existent sau creaz unul nou, sistemul Linux va returna un
descriptor de fiier unic. Programul va gestiona fiierul folosind descriptorul de
fiier respectiv. Tabelul 2.5 prezint i numele de identificare specifice limbajului
de programare C. De obicei, cnd cineva spune STDOUT nelegem c se refer la
descriptorul de fiier 1, adic la monitor. Pe de alt parte, STDERR semnific
destinaia la care programele trimit mesajele de eroare. Descriptorii de fiier 1 i 2
au aceeai destinaie, monitorul, i mesajele standard i cele de eroare vor fi afiate

n acelai mod. Totui, n sistemele Unix putem separa ieirile standard ale
programului de mesajele de eroare (sau alte mesaje specifice modului n care se
comport programul) printr-un mecanism numit redirecionare I/O.
Programul hello.asm folosete descriptorul de fiier STDOUT ca s afieze
un text pe ecranul terminalului. Urmtoarea valoare de intrare specific adresa de
la care ncepe irul care trebuie afiat. Observai c specificarea locaiei de
memorie se face prin adresare direct. Aceasta presupune ca n registrul ECX s se
afle adresa explicit a locaiei de memorie de la care ncepe textul mesajului.
Ultimul parametru de intrare specific lungimea irului care trebuie afiat. n loc s
specifice n clar numrul de octei ai mesajului, programul determin lungimea
acestuia printr-un mic artificiu prezentat deja n capitolul dedicat structurilor de
date. Aa cum reiese din pagina de manual a funciei de sistem WRITE, valoarea
returnat reprezint numrul de octei scrii sau o valoare negativ (-1) - dac
apelul s-a ncheiat cu o eroare. Valoarea returnat este plasat n registrul EAX.
Programatorul trebuie s verifice acest valoare, n special de posibilitatea apariiei
vreunei erori. ntotdeauna trebuie s inem cont de tipul de date al valorii returnate.
Unele funcii de sistem folosesc tipuri de date exotice. Acesta este i cazul nostru:
funcia WRITE returneaz o valoare de tip ssize_t. Tipul de date ssize_t nu
este unul din cele ntlnite n limbajul de asamblare. De fapt, nu ine de limbajul de
programare, ci este un sinonim pentru o valoare de tip ntreg folosit de sistemul de
operare Linux. Reprezint numrul de caractere scris la un descriptor de fiier sau
valoarea -1 dac apare vreo eroare.
Urmtorul program demonstreaz modul n care putem gestiona valorile
returnate de o funcie de sistem.
;
;CallReturn.asm
;
section .bss
pid resb 4
uid resb 4
gid resb 4
section .text
global _start
_start:
nop
mov eax,20
int 80h
mov [pid],eax
mov eax,24
int 80h

mov [uid],eax
mov eax,47
int 80h
mov [gid],eax
end:
mov eax,1
mov ebx,0
int 80h
Programul CallReturn.asm folosete trei apeluri de sistem separate:
Indicatorul de apel Funcia de sistem
20

getpid

24

getuid

47

getgid

Descriere
Specific identificatorul programului
care ruleaz
Specific identificatorul persoanei care
ruleaz programul
Specific identificatorul de grup al
persoanei care ruleaz programul

n urma plasrii identificatorilor de apel n registrul EAX i al execuiei


instruciunii INT pentru fiecare apel n parte, valoarea returnat n registrul EAX
este introdus ntr-o locaie de memorie cu nume sugestiv. Eticheta end ofer un
punct de ntrerupere uor de accesat, astfel nct s putem afia valorile de ieire
grupat.
(gdb) b *end
Breakpoint 1 at 0x80480a5: file return.asm, line 19.
(gdb) r
Starting program: /home/stefan/return
Breakpoint 1, end () at return.asm:19
(gdb) x /d &pid
0x80490b4 <pid>:
3975
(gdb) x /d &uid
0x80490b8 <uid>:
1000
(gdb) x /d &gid
0x80490bc <gid>:
1000
Identificatorul de proces este unic programului care ruleaz. Ceilali doi
identificatori se pot afla prin comanda Linux id.

id
uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu)
Urmtorul program permite introducerea datelor de la tastatur prin funcia
de sistem READ.
;
;readInput.asm
;
section .data
lung_nume dd
0
;;bufferul este zona de memorie n care vom stoca numele introdus, 40 de
caractere ASCII 0x2d
buffer
db
"--------------------------------------"
msg_cerere db
Introducei numele: ,0xa
msg_cerere_lung equ $ - msg_cerere
msg_salut db
"Salut, "
msg_salut_lung equ $ - msg_salut_lung
;;dac dorii s folosii pentru buffer memorie neiniializat, comentai linia
buffer din .data i activai urmtoarele dou linii.
;section .bss
;
buffer resb
40
section .text
global _start
_start:
nop
mov
mov
mov
mov
int

eax,4
ebx,1
ecx,msg_cerere
edx,msg_cerere_lung
80h

mov eax,3
mov ebx,0
mov ecx,buffer
mov edx,40
int 80h
;read returneaz n EAX numrul de caractere introduse de la tastatur

mov [lungime_nume],eax
mov
mov
mov
mov
int

eax,4
ebx,1
ecx,msg_salut
edx,msg_salut_lung
80h

mov
mov
mov
mov
int

eax,4
ebx,1
ecx,buffer
edx,[lungime_nume]
80h

mov eax,1
mov ebx,0
int 80h
Funcia de sistem READ este identificat prin cifra 3. Are nevoie ca
descriptorul de fiier s fie specificat n registrul EBX, iar adresa i dimensiunea
zonei de memorie la care vor fi stocate datele trebuie introdus n registrul ECX,
respectiv n EDX. Va returna numrul de caractere citit din fiier sau, n cazul n
care operaiunea nu s-a putut desfura (de ex., nu exist fiierul), un cod de eroare.
Codurile de eroare sunt ntodeauna reprezentate prin numere negative (-1).
Observai c funcia de sistem pentru scriere necesit aceeai parametrii ca funcia
de sistem pentru citire, cu excepia faptului c datele care vor fi scrise trebuie s se
afle deja n zona de memorie tampon (buffer).

10.2.1.

Alocarea static a bufferelor

n programul readInput.asm am folosit o variabil buffer fr s


explicm ce reprezint. Un buffer este un bloc continuu de octei folosit la
transferul datelor. La apariia unei cereri de citire fiier, sistemul de operare trebuie
s aib la dispoziie o zon de memorie n care s stocheze datele citite. Aceast
zon de memorie se numete buffer. De obicei, bufferele sunt utilizate numai
pentru stocarea temporar a datelor, date care ulterior sunt citite i convertite n
diferite formate necesare. De exemplu, n programul anterior am citit o linie de text
de la STDIN fr s tim exact lungimea acesteia. Am presupus c aceasta va avea
maxim 40 de caractere, de aceea am setat lungimea bufferului la 40 de caractere.
Dac textul ar fi depit 40 de caractere, surplusul de caractere ar fi fost ignorat.
Bufferele au mrime fix, setat de programator.

Buffer-ul este creat prin rezervarea static sau dinamic a unui spaiu de
memorie. Rezervarea dinamic nu face obiectul acestei cri. n program am folosit
dou modaliti statice de rezervare a spaiului. O prim modalitate a fost
declararea acestuia n zona datelor iniializate (prin directive standard DB, DW,
DD, DQ, DT). Totui, pe lng faptul c este destul de incomod s iniializezi cu o
valoare numrul respectiv de octei, o astfel de declaraie crete numrul de octei
ai executabilului. n exemplul anterior, mrimea este infim, dar presupunnd c
avem nevoie de un buffer de 1025 de octei, ambele probleme devin semnificative.
Soluia a fost deja dezvluit: rezervarea unui spaiu temporar de stocare n zona
datelor neiniializate. Soluia este foarte util deoarece nu trebuie s iniializm
zona respectiv cu o valoare i nici nu cretem suplimentar mrimea executabilului.
Directiva
buffer

resb

40

rezerv 40 de octei pe care i putem folosi ca buffer.

10.2.2.

Macroinstruciuni

Observm c programele care folosesc multe apeluri de sistem tind s


devin foarte lungi. Pentru fiecare apel de sistem n parte folosim un numr
considerabil de instruciuni. n capitolul precedent am vzut c putem mpri
programul n mai multe module prin intermediul funciilor. Mecanismul de apelare
i de revenire din funcii este implementat la nivelul procesorului i este
independent de asamblor. Dar asambloarele ofer i alt modalitate de management
a complexitii: macroinstruciunile.
Programele scrise n limbaj de asamblare conin trei tipuri de propoziii:
instruciuni, directive de asamblare i macroinstruciuni. Macroinstruciunile dau
posibilitatea substituirii unei secvene de program cu un nume generic.
n timp ce funciile folosesc instruciuni CALL i RET, macroinstruciunile
reprezint un artificiu pus la dispoziie de asamblor i nu depind de o anumit
instruciune sau de un anumit set de instruciuni. O macroinstruciune este o
etichet, un nume sub care se ascunde un bloc de text. Aceste linii de text pot
reprezenta, dei nu este neaprat necesar, o secven de instruciuni. Cnd
asamblorul ntlnete n fiierul surs o astfel de etichet, substituie numele cu
secvena de text asociat, proces numit extinderea macroinstruciunii.
Macroinstruciunile sunt definite cu directivele %macro i %endmacro,
sintaxa fiind urmtoarea:

%macro <nume_macro> <nr_parametri>


bloc de text
%endmacro
Nume_macro reprezint numele macroinstruciunii, iar nr_parametri
specific numrul de parametrii utilizai de aceasta. Apelul macroinstruciunii se
face prin specificarea numelui i valorii parametrilor. Pentru o macroinstruciune
fr parametrii, nr_parametri poate fi 0 sau poate lipsi. De exemplu,
urmtoarea macroinstruciune fr parametrii ntrerupe execuia programului:
%macro Exit
mov eax,1
mov ebx,0
int 80h
%endmacro
O macroinstruciune cu parametrii poate transfera date dintr-o locaie de
memorie n alt locaie de memorie. tim c transferul de date ntre locaii de
memorie nu este permis. De aceea, recurgem la un registru intermediar.
%macro mxchg 2
xchg eax, %1
xchg eax, %2
xchg %1, eax
%endmacro
Aceast macroinstruciune se invoc
mxchg valoare1, valoare2
i interschimb valorile fr modificarea registrului EAX. Parametrii
macroinstruciunii sunt reprezentai prin numere precedate de semnul % (procent).
Astfel, %1 indic primul parametru, %2 indic al doilea parametru. Valorile
propriu-zise primite de parametrii se numesc argumente. Nu confundai parametrii
cu valorile efective ale acestora. Parametrii sunt etichetele care urmeaz numelui n
linia n care este definit macroinstruciunea. Argumentele sunt valorile specificate
n linia care invoc macroinstruciunea.
Urmtorul program cere de la tastatur numele utilizatorului i afieaz un
mesaj de salut.
;
;salutMacro.asm

;
%macro Write 2
mov eax,4
mov ebx,1
mov ecx, %1
mov edx, %2
int 80h
%endmacro
%macro Read 2
mov eax,3
mov ebx,0
mov ecx,%1
mov edx,%2
int 80h
%endmacro
%macro Exit 0
mov eax,1
mov ebx,0
int 80h
%endmacro
section .data
cerere_nume
db
"Introduceti numele: "
cerere_size
equ $-cerere_nume
salut db
"Salut, "
salut_size equ $-salut
buffer
db
"01234567890123456789"
lungime
dd
0
nr_caractere
db
20
section .text
global _start
_start:
nop
Write cerere_nume,cerere_size
Read buffer,[nr_caractere]
mov [lungime],eax
Write salut,salut_size
Write buffer,[lungime]
Exit
Macroinstruciunile din programul salutMacro.asm sunt simple i uor de
interpretat. Niciuna nu conine intruciuni de salt. Dar codul din macroinstruciune,
la fel ca orice funcie, poate utiliza salturi condiionate sau necondiionate. Aici

apare ns o problem important: etichetele din programele de asamblare trebuie


s fie unice i, cum un macro este reintrodus n codul surs o dat cu fiecare
invocare, la asamblare vor aprea erori care semnalizeaz reutilizarea etichetelor.
Din aceast cauz, asamblorul YASM permite tratarea etichetelor din interiorul
macroinstruciunilor ca etichete locale. Etichetele locale nu au semnificaie dect n
cadrul macroinstruciunii care le-a definit. Faptul c etichetele locale unui macro
nu sunt vizibile din exteriorul su nseamn c ele nu pot fi adresate dect din
interiorul granielor %macro ... %endmacro.
Toate etichetele definite n cadrul unui macro sunt considerate locale i
interpretate special de asamblor. Urmtorul program citete de la tastatur
caractere scrise cu liter mic i le convertete n majuscule.
;
;majuscule.asm
;
%macro Majuscule 2
mov edx,%1
;%1 = Adresa buffer-ului
mov ecx,%2
;%2 = Numrul de caractere din buffer
%%LC: cmp byte [edx+ecx-1],'a' ;Sub 'a'?
jb %%Next
cmp byte [edx+ecx-1],'z' ;Deasupra lui 'z'?
ja %%Next
sub byte [edx+ecx-1],20h ;Transform octetul din buffer n
majuscul
%%Next:
dec ecx
jnz %%LC
%endmacro
%macro Write 2
mov eax,4
mov ebx,1
mov ecx, %1
mov edx, %2
int 80h
%endmacro
%macro Read
2
mov eax,3
mov ebx,0
mov ecx,%1
mov edx,%2
int 80h
%endmacro
%macro Exit
0
mov eax,1

mov ebx,0
int 80h
%endmacro
section .bss
BUFLEN
equ 1024
buffer
resb BUFLEN
section .text
global _start
_start:
nop
;citim un buffer de la stdin
citeste:
Read buffer,BUFLEN
mov esi,eax
cmp eax,0
je sfarsit
Majuscule buffer,eax
Write buffer,esi
jmp citeste
sfarsit:
;ntrerupem execuia programului
Exit
Eticheta din interiorul unei macroinstruciuni precedat de dou simboluri procent
%% devine local. Dac eticheta marcheaz o anumit poziie din macroinstruciune
trebuie urmat de dou puncte. Dac este folosit ca operand pentru instruciuni de
apel sau de salt (precum JA, JB i JNZ n programul anterior), nu trebuie urmat
de dou puncte. Este foarte important s nelegem c, dac etichetele Next i LC
nu erau marcate ca locale prin adugarea prefixului %%, n cadrul programului ar fi
existat mai multe instane de asemenea etichete (presupunnd c
macroinstruciunea ar fi fost invocat de mai multe ori) i asamblorul ar fi generat
o nou eroare de etichet duplicat ncepnd cu a doua apariie.
Modul n care asamblorul trateaz macroinstruciunile i etichetele locale
acestora se poate observa din fiierul majuscule.lst.
1
%line 31+1 majuscule.asm
2
3
[section .bss]
4
BUFLEN equ 1024
5 00000000 <gap>
buffer resb BUFLEN
6
[section .text]

7
8
9 00000000 90
10
11
12 00000001 B803000000
13
14 00000006 BB00000000
15 0000000B B9[00000000]
16 00000010 BA00040000
17 00000015 CD80
18
19 00000017 89C6
20 00000019 83F800
21 0000001C 7430
22
23 0000001E BA[00000000]
24
25 00000023 89C1
26 00000025 807C0AFF61
27 0000002A 720A
28 0000002C 807C0AFF7A
29 00000031 7703
30 00000033 806C0AFF20
31 00000038 49
32 00000039 75E8
33
34
35 0000003B B804000000
36
37 00000040 BB01000000
38 00000045 B9[00000000]
39 0000004A 89F2
40 0000004C CD80
41
42 0000004E EBAF
43
44 00000050 B801000000
45
46 00000055 BB00000000
47 0000005A CD80

[global _start]
_start:
nop
citeste:
mov eax,3
%line 41+0 majuscule.asm
mov ebx,0
mov ecx,buffer
mov edx,BUFLEN
int 80
%line 42+1 majuscule.asm
mov esi,eax
cmp eax,0
je sfarsit
mov edx,buffer
%line 46+0 majuscule.asm
mov ecx,eax
..@4.LC: cmp byte [edx+ecx-1],'a'
jb ..@4.Next
cmp byte [edx+ecx-1],'z'
ja ..@4.Next
sub byte [edx+ecx-1],20
..@4.Next: dec ecx
jnz ..@4.LC
%line 47+1 majuscule.asm
mov eax,4
%line 48+0 majuscule.asm
mov ebx,1
mov ecx, buffer
mov edx, esi
int 80
%line 49+1 majuscule.asm
jmp citeste
sfarsit:
mov eax,1
%line 51+0 majuscule.asm
mov ebx,0
int 80

Deoarece etichetele trebuie s fie unice, YASM transform eticheta local


marcat cu %% ntr-o etichet unic prin prefixarea acesteia cu ..@ plus un numr
de patru digii i numele etichetei. De exemplu, etichetele locale %%LC i %%Next
au devenit ..@4.LC, respectiv
..@4.Next. La o nou invocare a
macroinstruciunii, YASM ar fi modificat numrul i ar fi generat un nou sinonim
unic pentru fiecare etichet local.
La fel ca n cazul funciilor, macroinstruciunile pot fi grupate n biblioteci
externe programului. O bibliotec de macroinstruciuni nu este altceva dect un
fiier text ce conine codul surs al macroinstruciunilor. Spre deosebire de funciile
adunate ntr-un modul, bibliotecile de macroinstruciuni sunt asamblate separat i
trebuie parcurse la fiecare proces de asamblare a programului. Bibliotecile de
macroinstruciuni sunt introduse n program cu ajutorul directivei %include.
%include biblioteca.mac
Teoretic, aceast propoziie se poate afla oriunde n codul surs, att timp ct
definiiile macroinstruciunilor se gsesc naintea adresrii lor. Dar tocmai din acest
motiv cel mai indicat este s includem directiva foarte aproape de nceputul
fiierului surs. Dac biblioteca de macroinstruciuni nu se afl n directorul curent,
n cadrul directivei putem specifica calea absolut sau relativ.
%include ../libs/biblioteca.mac
Dac asamblorul nu poate localiza fiierul cu macroinstruciuni va genera un mesaj
de eroare.
Tot din listingul majuscule.lst am vzut c de fiecare dat cnd este
invocat macroinstruciunea toate instruciunile acesteia sunt reintroduse n
program. Dimensiunea programului va crete simitor pentru un numr
semnificativ de apeluri. Odat cu dimensiunea crete i memoria ocupat de
proces. n programul salutMacro.asm cele trei apeluri ale macroinstruciunii
Write genereaz un total de cincisprezece instruciuni. Dac secvena de
instruciuni ar fi fost asamblat ca funcie, ar fi fost necesare numai cinci
instruciuni, plus un RET i trei instruciuni CALL. Dar atunci cnd programul
trebuie s fie ct mai rapid cu putin, macroinstruciunile au avantajul c elimin
ncrcarea suplimentar generat de apelurile i revenirile din funcii. Viteza este
marele avantaj al macroinstruciunilor.

10.2.3.
Funcii de lucru cu transferuri de
intrare/ieire
Macroinstruciunile i funciile pot fi combinate. n acest moment deinem
destule cunotine pentru a putea scrie cteva funcii care mijlocesc interaciunea
noastr cu sistemul: citirea unui ir de caractere de la tastur, afiarea unui ir de
caractere la monitor, etc..
Scriem urmtoarele linii ntr-un fiier numit io.inc i salvm.
extern f_ReadStr f_WriteStr
extern f_nwln
%macro

ReadStr
push
push
mov
mov
call
pop
pop
%endmacro

1-2 101
ESI
EDI
EDI,%1
ESI,%2
f_ReadStr
EDI
ESI

%macro

WriteStr 1
push
ECX
mov
ECX,%1
call
f_WriteStr
pop
ECX
%endmacro
%macro

nwln
call
%endmacro

0
f_nwln

%macro WriteChar
mov eax,4
mov ebx,1
mov ecx,%1
mov edx,1
int 80h
%endmacro
%macro

ReadChar

mov eax,3
mov ebx,0
mov ecx,%1
mov edx,1
int 80h
%endmacro
%macro Exit
mov eax,1
mov ebx,0
int 80h
%endmacro

Eticheta 1-2 din linia macroinstruciunii ReadStr nseamn c al doilea


parametru al acesteia poate lipsi. Dac lipsete (nu este specificat), valoarea
implicit este 101. ReadStr nu este dect o interfa pentru funcia f_ReadStr.
Dar vom vedea c folosirea unui astfel de mecanism aduce avantaje. Funcia
f_ReadStr citete un ir de caractere de la tastatur i l stocheaz ntr-un buffer.
Primete doi parametri: numele bufferului i numrul de caractere ce trebuie citit.
Acesta din urm poate lipsi, macroinstruciunea are grij s l seteze la o valoare
implicit.
;
;funcia f_ReadStr
;
section .bss
temp_str resb
256
section .text
global f_ReadStr
f_ReadStr:
pushad
pushf
mov eax,3
;funcia de sistem READ
mov ebx,0
;STDIN
mov ecx,temp_str
;buffer temporar
mov edx,0x100
;count = 256 de caractere
int 80h
;;n ESI se afl numrul de caractere - parametrul %2 din macro.
mov ecx,esi
dec ecx
;se scade un caracter (spaiu pentru NULL)
;;n EDI se afl adresa bufferului DESTINATIE dat n macro.

mov ebx,edi
mov esi,temp_str ;adresa de nceput pt. buffer-ul intern funciei
.bucla:
mov al,byte [esi]
;ncarc n AL primul caracter din irul
introdus
cmp al,0xa
;verifica dac utilizatorul a tastat ENTER
je .sfarsit
;ENTER nseamn sfritul irului
mov byte [ebx],al
;copiaz caracterul n DESTINATIE
inc ebx
;urmtorul octet din DESTINATIE
inc esi
;urmatorul caracter din buffer-ul intern
loop .bucla
;repet pn cnd irul s-a sfrit
.sfarsit:
mov byte [ebx],0x0
;adaug caracterul NULL
popf
popad
ret
Funcia f_ReadStr citete de la tastatur un ir de caracterele pe care l ncheie
cu un caracter NULL. Funcia f_WriteStr efectueaz operaia invers: scrie la
monitor un ir de caractere terminat n NULL. Primete un singur parametru:
adresa de la care ncepe irul de afiat. Declararea funciei f_WriteStr se face
tot n fiierul io.inc.
;
;funcia f_WriteStr
;
section .text
global f_WriteStr
f_WriteStr:
pushad
pushf
mov ebx,1
;STDOUT
mov edx,1
;count = 1 caracter
.repeta:
mov eax,4
;funcia de sistem WRITE
;;n ECX este adresa sirului surs. Parametrul %1 din macro.
cmp byte [ecx],0x0
;verific dac este caracterul NULL
je .sfarsit
;dac este NULL, irul s-a sfrit
int 80h
;afieaz caracterul

inc ecx
jmp .repeta
.sfarsit:
popf
popad

;urmtorul caracter
;se iese din bucl numai cnd ECX = 0

ret
Obsevai c etichetele din cadrul funciilor ncep cu punct. Acest punct marcheaz
eticheta ca fiind local (similar celor dou simboluri procent din
macroinstruciuni). n programe, eticheta local permite utilizarea aceluiai cuvnt
n zone de cod diferite. Etichetele cu punct sunt locale fa de prima etichet
global (etichetele care nu ncep cu punct) care le precede. Din acest motiv, o
etichet local este vizibil numai dup eticheta global creia i aparine. n
consecin, eticheta local nu poate fi adresat de instruciuni aflate naintea
etichetei globale corespunztoare (i care, repetm, este prima etichet global
aflat naintea etichetei locale). n cazul nostru, deoarece le folosim n corpul
funciilor, pare c etichetele locale nu sunt precedate de nicio etichet global. Dar,
ntr-un program, numele funciilor nu reprezint altceva dect etichete globale.
Etichetele locale din cadrul funciilor sunt cel puin locale funciilor n care sunt
definite. Firete, putem folosi etichete globale i n corpul funciilor, lucru care
limiteaz i mai mult vizibilitatea etichetei locale.
Funcia f_nwln trimite la monitor caracterul \n.
;
;funcia f_nwln
;
section .data
new_line
db
0xa
section .text
global f_nwln
f_nwln:
pushad
mov eax,4
;sys_write
mov ebx,1
mov ecx,new_line
mov edx,1
int 80h
popad
ret

Nume

Operand Descriere
Afieaz un ir terminat n NULL adresat de
WriteStr surs
m
eticheta surs.
Citete Nr. de caractere de la tastatur i le
ReadStr dest[,Nr.]
m
stocheaz n dest ca ir terminat n NULL.
nwln
Afieaz caracterul de linie nou.

Urmtorul program citete un caracter de la tastatur i afieaz codul ASCII n


hexazecimal.
;
;hex.asm
;
%include "io.inc"
section .data
prompt db "Introduceti un caracter: ",0
mesaj1 db "Codul ASCII pentru '",0
mesaj2 db "'in hexa este ",0
mesaj3 db "Introduceti un nou caracter? (y/n)",0
hexTable db "0123456789ABCDEF"
section .bss
caracter resb 1
section .text
global _start
_start:
nop
citeste:
WriteStr prompt
ReadChar caracter
WriteStr mesaj1
WriteChar caracter
WriteStr mesaj2
mov al,[caracter]
mov ah,al
mov ebx,hexTable
shr al,4
xlatb
mov [caracter],al
;pushad
WriteChar caracter
;popad
mov al,ah

and al,0x0f
xlatb
mov [caracter],al
WriteChar caracter
nwln
Exit
Instruciunea XLATB nlocuiete caracterul din registrul AL cu un octet din tabela
hexTable. Adresa de nceput a tabelei se ncarc, n prealabil, n EBX.
Coninutul registrului AL este tratat ca index n aceast tabel de conversie
(translaie). Valoarea octetului corespunztor indexului nlocuiete valoarea index
din AL. Instruciunea se utilizeaz ndeosebi pentru conversii de cod.
Observai c programul ncepe cu directiva %include io.inc. Aceasta
foreaz asamblorul s includ n codul surs coninutul fiierului io.inc din
directorul curent. Efectul se poate observa n fiierul .lst. Aducei-v aminte c
YASM difereniaz caracterele mici de cele mari.
Pentru a genera executabilul trebuie s asamblm pe rnd funciile i
programul principal, apoi s editm legturile:
yasm -f elf -g stabs f_ReadStr.asm
yasm -f elf -g stabs f_WriteStr.asm
yasm -f elf -g stabs f_nwln
yasm -f elf -g stabs hex.asm
ld -o hex hex.o f_WriteStr.o f_nwln.o f_ReadStr.o
melf_i386
Programul nu funcioneaz corect. Dac depanai programul cu GDB devine
evident c problema este legat de rescrierea registrului AH n corpul celei de a
doua macroinstruciuni WriteChar. Pierdem caracterul introdus de la tastatur.
Macroinstruciunile sunt simple secvene de cod introduse n program de fiecare
dat cnd scriem numele acestora. Cu ct programele sunt mai complexe i
folosesc mai multe registre, cu att crete posibilitatea s greim.
n cazul acestui program putem salva coninutul registrelor nainte de
intrarea n macroinstruciune cu PUSHAD. Avem i alte posibiliti, putem salva
numai coninutul registrului EDX sau putem include instruciunile de salvare i
recuperare a registrelor chiar n corpul macroinstruciunii. Dar, o soluie mai
elegant este s folosim funcii. Modificm nregistrrile corespunztoare
macroinstruciunilor ReadChar i WriteChar din io.inc astfel:
%macro

WriteChar

push EAX
mov AL,byte [%1]
call f_WriteChar
pop EAX
%endmacro
%macro ReadChar 1
push EAX
call f_ReadChar
mov byte [%1],AL
pop EAX
%endmacro
Crem dou fiiere f_ReadChar.asm i f_WriteChar.asm n care
introducem instruciunile funciilor de citire, respectiv afiare, caracter.
section .bss
temp_char resb 256
section .text
global f_ReadChar
f_ReadChar:
pushf
mov eax,3
mov ebx,0
mov ecx,temp_char
mov edx,1
int 80h
mov al,byte [temp_char]
popf
ret

section .bss
temp_char resb 256
section .text
global f_WriteChar
f_WriteChar:
pushad
pushf
mov byte
mov
mov
mov
mov
int

[temp_char],al
eax,4
ebx,1
ecx,temp_char
edx,1
80h

popf
popad
ret

Nume
WriteChar surs
ReadChar dest

Operand
Descriere
Afieaz un caracter din surs.
m
Citete un caracter n dest.
m

Reluai procesul de asamblare. Nu uitai s introducei n linia editorului de legturi

fiierele obiect corespunztoare acestor ultime dou funcii. Listingul programului


este tot hex.asm, varianta fr instruciuni PUSHAD i POPAD.

10.2.4.

Operaii cu fiiere

Am vzut c funciile de sistem WRITE i READ pot scrie informaii la


monitor sau pot citi date de la tastatur pentru c aceste dispozitive sunt fiiere
deschise implicit pentru orice program care ruleaz. STDIN este un fiier asupra
cruia avem numai drepturi de citire (reprezint tastatura), STDOUT este un fiier
asupra cruia avem numai drept de scriere (reprezint monitorul). nseamn c
putem folosi aceste apeluri de sistem pentru orice tip de fiier, chiar i pentru un
fiier neles n sensul clasic (date salvate pe disc). De exemplu, programul
majuscule.asm poate converti caracterele unor fiiere aflate pe disc prin
mecanismul de redirecionare I/O.
./majuscule < fisier.txt > FISIER.txt
Numai c, spre deosebire de cele trei fiiere standard, deschise ntodeauna de
sistemul de operare la nceputul rulrii unui program, fiierele aflate pe disc sunt
nchise. Pentru a putea citi sau scrie date n ele, programul trebuie s le deschid.
Evident, acest lucru se face printr-o funcie de sistem. Funcia OPEN primete ca
argumente: numele fiierului, un numr ce reprezint modul de operare asupra
fiierului deschis (citire, scriere, citire i scriere, dac nu exist trebuie creat, etc..)
i un set de permisiuni (reamintim c permisiunile se refer la cine are dreptul s
acceseze respectivul fiier - utilizatorul, grupul din care face parte utilizatorul sau
acces universal-, i pot fi verificate cu comanda ls).
n programele care urmeaz vom gestiona fiierele n modul urmtor:
Specificm sistemului de operare numele fiierului pe care dorim s l
deschidem i modul de operare asupra lui. Realizm acest lucru cu
apelul de sistem OPEN,care are ca identificator (n registrul EAX)
numrul 5. Adresa primului caracter din fiier trebuie stocat n EBX.
Intenia de citire/scriere, reprezentat ca numr, trebuie stocat n
registrul ECX. Deocamdat folosim 0 pentru fiierele din care dorim s
citim, ali indicatori sunt prezentai n Tabelul 10.1 (dac se dorete
combinarea acestora se efectueaz un SAU logic ntre valorile lor). n
final, setul de permisiuni trebuie trecut n registrul EDX.
Sistemul de operare va returna n registrul EAX un descriptor de fiier.
Ne amintim c descriptorul de fiier este un numr folosit n program
ca referin pentru acel fiier, un identificator (ca i cnd ne-am referi
la o persoan folosind CNP-ul i nu numele).

Citim i/sau scriem fiierul, specificnd de fiecare dat descriptorul


dorit.
La finalul operaiilor, fiierele sunt nchise cu funcia de sistem
CLOSE, apelul de sistem 6. Singurul parametru necesar este
descriptorul de fiier; plasat n registrul EBX. Dup CLOSE,
descriptorul de fiier nu mai este valid.

Programul urmtor ilustreaz citirea unui fiier, stocarea coninutului ntrun buffer i afiarea acestuia pe ecran. Observai faptul c pentru a stoca datele
fiierului avem nevoie de un buffer. De asemenea, la citirea unui fiier trebuie
specificat cantitatea de informaie pe care dorim s o citim, iar aceast cantitate nu
poate depi mrimea buffer-ului. Numrul de octei citit din fiier (returnat de
funcia de sistem) este salvat ntr-o variabil (nrCitit). Fiierul este creat nainte
de rularea programului cu comanda:
echo Acesta este un fisier text > fisier.txt
;
;citireFisier.asm
;
section .data
numeFisier db fisier.txt,0
descriptor dd
0
nrCitit
dd
0
msgErr
db Nu am putut deschide fiierul,0xa
msgErr_lung
equ $ - msgErr
section .bss
MAXBUF
equ 100000
buffer
resb MAXBUF
section .text
global _start
_start:
nop
mov eax,5 ;identificatorul open
mov ebx,numeFisier
mov ecx,0 ;O_RDONLY
mov edx,0644q
int 80h
;open returneaz -1 n eax n caz de probleme
test eax,eax
jns readFile
;dac sunt probleme, TEST EAX,EAX seteaz SF, afim mesaj de eroare i

exit
mov
mov
mov
mov
int
mov
mov
int

eax,4
ebx,1
ecx,msgErr
edx,msgErr_lung
80h
eax,1
ebx,0
80h

readFile:
;salvm descriptorul de fiier
mov [descriptor],eax
;citim fiierul
mov eax,3
mov ebx,[descriptor]
mov ecx,buffer
mov edx,MAXBUF
int 80h
;salveaz numrul de octei citii
mov [nrCitit],eax
;afim buffer-ul
mov eax,4
mov ebx,1
mov ecx,buffer
mov edx,[nrCitit]
int 80h
;nchidem fiierul
mov eax,6
mov ebx,descriptor
int 80h
;ncheiem execuia programului
mov eax,1
mov ebx,0
int 80h
Tabelul 10.2 Moduri de operare asupra fiierelor de ctre funcia de sistem OPEN

Indicator
Nr. n octal
O_RDONLY
00000
O_WRONLY
00001
O_RDWR
00002

Descriere
Deschide fiierul numai n modul citire.
Deschide fiierul numai n modul scriere.
Deschide fiierul att pentru citire ct i pentru

O_CREAT
O_TRUNC
O_APPEND

00100
01000
02000

scriere.
Dac nc nu exist, creaz fiierul.
Dac fiierul exist, terge coninutul acestuia.
Adaug coninut. Scrie la sfritul fiierului, nu la
nceput.

;
;creareFisier.asm
;
section .data
msgErr

db Nu am putut crea fiierul,0xa

msgErr_lung
equ $ - msgErr
numeFisier db
date.txt
descriptor dd
0
buffer
db
What do you think about that?,0xa
db
I think you're alive...,0xa
buffer_lung
equ $ - buffer
section .text
global _start
_start:
nop
;creaz fiierul
mov eax,8 ;identificatorul funciei de sistem create
mov ebx,numeFisier
mov ecx,0644q
;permisiunile fiierului creat
int 80h
test eax,eax
jns createFile
;afieaz mesaj de eroare n caz c nu a putut fi creat
mov eax,4
mov ebx,1
mov ecx,msgErr
mov edx,msgErr_lung
int 80h
mov eax,1
mov ebx,0
int 80h
;n caz de succes salveaz descriptorul ntors de create n registrul EAX
createFile:
mov [descriptor],eax
;scrie n fiier

mov eax,4
mov ebx,[descriptor]
mov ecx,buffer
mov edx,buffer_lung
int 80h
;nchide fiierul
mov eax,6
mov ebx,[descriptor]
int 80h
;ieire din program
mov eax,1
mov ebx,0
int 80h

10.2.5.

Argumente n linia de comand

n capitolul anterior am studiat modul n care funciile primesc parametri


de intrare din partea programului principal. Dar i programul principal poate primi
parametri de intrare de la utilizator. Am vzut c aproape toate comenzile Linux
primesc opiuni n linie de comand.
Modalitatea de transfer a parametrilor de intrare ctre programe difer de la
un sistem de operare la altul. nainte s explicm metoda utilizat de Linux trebuie
s ne amintim cum este structurat memoria principal din perspectiva unui proces.
Cnd un program este lansat n execuie, sistemul de operare creaz pentru acesta o
zon de memorie cu aspectul dat n Figura 10.1. Stiva programului, destinat
stocrii variabilelor locale i a parametrilor pentru funcii, este reprezentat de
blocul superior de memorie. Dei am putea crede c la nceputul procesului stiva
este liber (spaiu gol), acest lucru nu este adevrat. nainte s ncarce programul n
memorie, sistemul de operare plaseaz n stiv urmtoarele informaii:
numrul de parametrii din linia de comand;
numele i calea absolut a programului, aa cum a fost el executat din
linia de comand;
fiecare parametru (argument) introdus de utilizator n linia de comand
la execuia programului;
toate variabilele contextului de lucru la nceputul rulrii programului.
Toate sunt plasate n memorie conform planului prezentat n Figura 10.2.
Numele programului, argumentele din linia de comand i variabilele de mediu
sunt iruri de lungime variabil terminate n caracterul NULL (nume care denot
32 de bii de 0). Linux nu numai c ncarc irurile n stiv, dar ncarc i adresele
(pointeri) acestora.

4 GB

0FFFFFFFFH

KERNEL SPACE

Memoria virtual a
kernelului
(cod, date, heap, stiv)

3 GB

0BFFFFFFFH
Stiv program

Bloc superior

ESP

Biblioteci partajate
040000000H
USER SPACE
Heap
section .bss

Bloc inferior

section .data

ncrcate din
fiierul executabil

section .text

08048000H
00000000H

Figura 10.2 Structura unui process n Linux

La nceputul execuiei, registrul ESP indic vrful stivei. Deasupra adresei


din ESP se gsesc toate elementele introduse de sistemul de operare, urmnd ca
variabilele locale i parametrii funciilor (cadrele de stiv) s fie dispuse la adrese
mai mici dect cea din ESP. ncepnd cu indicatorul de stiv (ESP), urmeaz
numrul parametrilor din linia de comand sub forma unui ntreg fr semn de 4
octei, adresa locaiei la care se afl numele programului, adresele fiecrui
parametru din linia de comand (fiecare cte 4 octei). Lista argumentelor se
termin cu un indicator NULL. De la acesta n sus ncepe o list lung de adrese pe
32 de bii, fiecare indic un ir, terminat tot n NULL, ctre variabilele mediului de

lucru. Numrul acestora difer de la sistem la sistem, dar poate fi aproape de 200.
La finalul listei de adrese pentru variabilele de mediu este nc un indicator. Acesta
marcheaz sfritul directorului de stiv.
La primele sisteme Linux adresa de nceput a stivei era ntotdeauna
0BFFFFFFFH. n prezent, din considerente de securitate, sistemele Linux schimb
puin baza stivei odat cu fiecare rulare a programelor. De aceea, adresele de
nceput ale stivei pot diferi de la rulare la rulare, de obicei cu cteva milioane de
octei. n plus, ntre sfritul listei de adrese i nceputul irurilor de elemente se
afl o zon variabil de memorie neutilizat.
Structura stivei poate fi observat cu ajutorul depanatorului. Rulm
programul prezentat n seciunea anterioar: gdb program. Am vzut c
programul nu este lansat imediat n execuie, ci este inut n memorie pn cnd
0x00000000
Calea absolut a executabilului
Variabilele mediu propriu-zise
(fiecare reprezentat ca ir de caractere terminat cu
NULL)
Argumentele din linia de comand propriu-zise
(fiecare reprezentat ca ir de caractere terminat cu
NULL)
Numele programului
Variabile de sistem i spaiu liber
0x00000000
Adresa variabilei de mediu 3
Adresa variabilei de mediu 2
Adresa variabilei de mediu 1
0x00000000
Adresa argumentului 3
Adresa argumentului 2
Adresa argumentului 1
Adresa numelui de progam
Numr de argumente
0

31

ESP

Figura 10.3 Stiva programului la momentul lansrii n execuie

dm comanda de rulare run. Acest lucru ne d posibilitatea s specificm punctele


de ntrerupere. Totodat, ne d posibilitatea s specificm argumentele din linia de
comand cu ajutorul comenzii set args.
(gdb) set args 10 20
(gdb) show args

Argument list to give program being debugged when it is started is "10 20".
n acest caz am introdus dou argumente: numerele 10 i 20. Chiar dac programul
nu folosete argumente din linie de comand, putem simula introducere lor n stiv.
Verificarea argumentelor introduse se face cu show args. O alt posibilitate este
s le specificm imediat dup inia de rulare. Nu uitai ca n prealabil s setai
punctul de oprire.
(gdb) b *_start+1
Breakpoint 1 at 0x8048081: file main.asm, line 9.
(gdb) r 10 20
Starting program: /home/stefan/program 10 20
Breakpoint 1, _start () at main.asm:9
9
push 5
(gdb) p $esp
$1 = (void *) 0xffffd400
Observai c linia Starting program indic prezena argumentelor din linia de
comand. Indicatorul de stiv ESP conine adresa vrfului stivei, 0xffffd400.
Afim primele 20 de cuvinte ncepnd cu aceast adres, n format hexazecimal.
(gdb) x /20xw 0xffffd400
0xffffd400:
0x00000003 0xffffd580
0xffffd410:
0x00000000 0xffffd59b
0xffffd420:
0xffffd5f3
0xffffd603
0xffffd430:
0xffffd670
0xffffd69a
0xffffd440:
0xffffdbb6
0xffffdbdc

0xffffd595
0xffffd5bd
0xffffd60e
0xffffd6ba
0xffffdc0e

0xffffd598
0xffffd5d0
0xffffd65e
0xffffd6c6
0xffffdc1e

Prima valoare, 0x00000003, specific numrul de argumente din linia de


comand, incluznd i numele programului. Urmtoarele dou locaii conin
indicatori ctre numele programului i irurile de parametrii din linia de comand.
Putem vedea valoarea acestor iruri tot prin intermediul comenzii x, urmat de
adresele respective.
(gdb) x /s 0xffffd580
0xffffd580:
"/home/stefan/program"
(gdb) x /s 0xffffd595
0xffffd595:
"10"
(gdb) x /s 0xffffd598
0xffffd598:
"20"

Atenie, este important s reinem c toate argumentele din linia de comand sunt
specificate ca iruri, chiar dac arat ca numere.
Argumentele din linia de comand sunt urmate de un indicator NULL care
separ adresele argumentelor de adresele variabilelor de mediu. Listm cteva
variabile de mediu pe baza adreselor lor.
(gdb) x /s
0xffffd59b:
(gdb) x /s
0xffffd5bd:
(gdb) x /s
0xffffd5d0:
(gdb) x /s
0xffffd5f3:
(gdb) x /s
0xffffd603:
(gdb) x /s
0xffffd65e:

0xffffd59b
"ORBIT_SOCKETDIR=/tmp/orbit-stefan"
0xffffd5bd
"SSH_AGENT_PID=1188"
0xffffd5d0
"GIO_LAUNCHED_DESKTOP_FILE_PID=4500"
0xffffd5f3
"SHELL=/bin/bash"
0xffffd603
"TERM=xterm"
0xffffd65e
"WINDOWID=73400324"

La rndul lor, acestea se vor sfri cu un indicator NULL. Numrul lor depinde de
aplicaiile prezente n sistem.

10.3. Operaii aritmetice n reprezentri ASCII i


BCD
Aa cum reiese din paragrafele precedente, argumentele din linia de
comand sunt dispuse n stiv sub forma unor iruri de caractere. Dac dorim s le
utilizm ca numere trebuie s le convertim. Conversia caracterelor la valori ntregi
sau n virgul mobil se poate face n mai multe moduri. Pentru operaii simple (de
ex., o singur adunare), Intel a pus la dispoziie instruciuni ce mijlocesc efectuarea
calculelor direct n format ASCII sau ntr-un format asemntor celui zecimal,
numit BCD (Binary Coded Decimal). Formatul BCD este destinat s simplifice
lucrul cu dispozitivele care utilizeaz numere zecimale (dispozitive care trebuie s
afieze numere, precum ceasuri sau numrtoare). BCD permite procesorului s
efectueze calcule dup un algoritm asemntor celui zecimal. n prezent,
instruciunile BCD sunt folosite rar, dar cunoaterea lor i a modului n care sunt
folosite de procesor poate fi util.

10.3.1.

Formatul BCD

Formatul Zecimal Codat Binar realizeaz exact ce i denot numele:


codific numerele zecimale n format binar. n funcie de numrul de bii necesar
reprezentrii unei cifre zecimale, 4, respectiv 8 bii, are dou variante, compactat i
necompactat. Fiecare valoare BCD este un ntreg de 4 sau 8 bii, cu o valoare ntre
0 i 9. Valorile mai mari de 9 sunt considerate invalide. n esen, regulile sunt:
fiecare cifr zecimal este nlocuit de 4 sau 8 bii;
se utilizeaz cifrele zecimale 0,1,2,3,4,5,6,7,8,9;
valorile binare superioare lui 9 sunt interzise.
Tabelul 10.3 Formatul BCD

Zecimal
0
1
2
3
4
5
6
7
8
9

Binar
0
1
10
11
100
101
110
111
1000
1001

BCD compactat
0000
0001
0010
0011
0100
0101
0110
0111
1000
1001

BCD necompactat
00000000
00000001
00000010
00000011
00000100
00000101
00000110
00000111
00001000
00001001

Regula prin care se formeaz un numr n BCD este regula zecimal, de


aici i numele de zecimal codat binar cifrele sunt reprezentate n binar, dar
regulile sunt zecimale. Pur i simplu, ca s obinem numrul 11 n BCD, alturm
reprezentrile binare ale celor dou cifre: 0001 0001.
Zecimal
15
100
99

10.3.2.

BCD compactat
0001 0101
0001 0000 0000
1001 1001

BCD necompactat
00000001 00000101
00000001 00000000 00000000
00001001 00001001

Erori i corecii n ASCII

Presupunem c trebuie s scriem un mic program de calcul aritmetic.


Acesta primete ca argumente n linie de comand dou cifre i un semn +, -, *, /.

Ordinea de introducere a argumentelor este cea convenionl: 5 + 3, 9 - 7, etc..


Pentru moment prezentm numai modulul de adunare i scdere (pentru un
listing mai scurt). Structura programului poate fi gandit astfel:
;
;calc.asm
;
section .data
nwln db
0ah
section .bss
rez resw 1
section .text
global _start
_start:
nop
mov esi,[esp+8] ;ncrcm n ESI adresa primei cifre
mov edi,[esp+16] ;ncrcm n EDI adresa celei de a doua cifre
mov ecx,[esp+12] ;adresa semnului aritmetic
mov dl,'+'
cmp dl,byte [ecx]
;comparam semnul aritmetic cu +
jne .L1
;; modulul de adunare
mov al,[esi]
add al,[edi]
jmp .end
.L1:
mov cl,'-'
;comparm semnul aritmetic cu cmp cl,byte [esp+12]
jne .L2
;; modulul de scdere
jmp .end
.end:
;; afiare rezultat
mov eax,4
mov ebx,1
mov ecx,rez
mov edx,2
int 80h
;; new line
mov eax,4
mov ebx,1
mov ecx,nwln

mov edx,1
int 80h
;; exit
mov eax,1
mov ebx,0
int 80h
La final, din motive pur estetice, funcia de afiare a rezultatului este urmat de
funcia de trecere la linie nou. Executabilul primete argumente din linie de
comand astfel:
./calc 3 + 5
Fiecare argument este desprit prin spaiu. Cnd rulm programul prin intermediul
depanatorului, pentru specificarea argumentelor folosim comanda set args 3
+ 5.
Primele trei instruciuni ale programului ncarc adresele argumentelor n
trei registre de uz general (n vederea adresrii lor prin metoda indirect).
(gdb) p /x $esi
$1 = 0xffffd598
(gdb) x /s $esi
0xffffd598:
"3"
(gdb) x /s $edi
0xffffd59c:
"5"
(gdb) x /s $ecx
0xffffd59a:
"+"
Pe baza semnului se alege modulul aritmetic corespunztor. n consecin,
argumentul de semn este comparat pe rnd cu fiecare semn n parte. Codul
semnului putea fi introdus n instruciune direct ca numr hexazecimal, dar am ales
formatul caracter ASCII, deoarece acesta este subiectul acestei seciuni.
(gdb) p /x $dl
$5 = 0x2b
(gdb) p /c $dl
$6 = 43 '+'
Adunarea ASCII
Problemele ncep imediat dup instruciunea de adunare.

(gdb) n
17
mov al,[esi]
(gdb) n
18
adc al,[edi]
(gdb) n
19
jmp .end
(gdb) p /x $eax
$7 = 0x68
Aa cum reiese din exemplul de mai jos, instruciunea adun corect caracterele
ASCII 3 (33H) i 5 (35H).
33H = 0011 0011 +
35H = 0011 0101
68H = 0110 1000
ns 68H reprezint caracterul ASCII 'h', nu caracterul '8' (38H).
Pe de alt parte, valorile 33H i 35H, privite din perspectiva formatului
BCD compactat, sunt valori valide pentru cifrele 3 i 5. La fel i 68H
reprezentare valid pentru cifra 8. Digitul inferior reprezint valoarea cifrei (3, 5,
respectiv 8), iar digitul superior este ignorat (l putem considera 0, i atunci avem
03 + 05 = 08). De fapt, deoarece cei 4 bii inferiori conin valoarea BCD, toi digiii
ASCII de la '0' la '9' pot fi considerai digii BCD valizi. n consecin, n BCD
compactat, adunarea 33H + 35H = 68H este corect. Rezultatul poate fi afiat cu
uurin dac setm digitul superior la valoarea 3. Totui, acest mecanism nu poate
fi aplicat n cazurile n care digitul rezultat este mai mare ca 9. S considerm
adunarea '7' + '5'.
37H = 0011 0111 +
35H = 0011 0101
6CH = 0110 1100
Chiar dac ignorm digitul superior (6), suma tot trebuia s fie de forma 01 02H.
Iat un prim caz de eroare n BCD. Adunarea a dou cifre valide are ca rezultat o
combinaie invalid (necunoscut). n acest caz, 1100 (C). Principiul coreciei n
BCD susine ca, n astfel de cazuri, digitul cu combinaie inexistent trebuie adunat
cu 0110 (6 n zecimal). 6 este diferena ntre baza hexazecimal i cea zecimal.
0000 1100 +
0000 0110
0001 0010

Aadar, dac am avea posibilitatea s adunm n BCD, adunarea '7' + '5' ar da ntradevr '12', deoarece '7' i '5' pot fi tratate ca valori BCD legale. Ei bine, Intel pune
la dispoziie o astfel de instruciune: instruciunea AAA (ASCII Adjust after
Addition). Instruciunea AAA se folosete dup o adunare efectuat cu instruciunea
ADD sau ADC. Suma rezultat n registrul AL este convertit n reprezentare BCD
n doi pai:
dac digitul inferior din registrul AL este mai mare ca 9, sau dac este
setat indicatorul de condiii AF din registrul EFLAGS, adun 6 la AL
i 1 la AH (n urma operaiei, indicatorii de condiii AF i CF sunt
setai).
n toate cazurile, digitul superior este trecut n zero.
n aceste condiii, modulul de adunare din programul calc.asm poate fi
scris astfel:
;; modulul de adunare
mov al,[esi]
adc al,[edi]
aaa
or ax,3030h
cmp ah,30h
jne L0_1
mov [rez],al
jmp .end
L0_1:
xchg ah,al
mov [rez],ax
jmp .end
Pentru a putea afia rezultatul, instruciunea OR AX,3030H seteaz
pentru fiecare cifr BCD digitul superior la 3. Se obine un ir de dou caractere
ASCII. n cazul adunrii '7' + '5' irul ASCII din AX este 3132H. Compararea lui
AH cu 30H are urmtoarele dou scopuri:
dac rezultatul poate fi reprezentat cu un singur caracter, nu includem
n faa acetuia caracterul 0 (pentru 4 + 4 afim 8, nu 08);
dac rezultatul este reprezentat pe dou caractere, trebuie s inem cont
de ordinea corect a octeilor. Octetul superior (AH) este trecut n
memorie la adres superioar, octetul inferior (AL) este trecut n
memorie la adres inferioar. Primul afiat va fi AL.
Dac dorim s adunm un numr cu mai muli digii, trebuie s folosim o

bucl ce adun digit cu digit, ncepnd cu primul din dreapta.


Scderea ASCII
Corecia rezultatului obinut n urma unei operaii de scdere se face cu
instruciunea AAS (ASCII Adjust after Substraction). Aciunile sale sunt:
dac digitul inferior din AL este mai mare ca 9, sau indicatorul de
condiii AF este setat, scade 6 din AL i 1 din AH (n urma operaiei,
indicatorii de condiii AF i CF sunt setai).
n toate cazurile, digitul superior este iniializat cu zero.
Este evident c ajustarea este necesar numai n cazul unui rezultat negativ.
Dac rezultatul este pozitiv, coninutul registrului AL nu este afectat de
instruciunea AAS.
Scdere ASCII cu rezultat pozitiv:
xor ah,ah
mov al,'8'
sub al,'3'
aas
or al,30h

;terge registrul AH
;AL = 38H
;AL = 38H 33H = 05H
;AX = 0005H
;AL = 35H

Scdere ASCII cu rezultat negativ:


xor ah,ah
mov al,'3'
sub al,'8'
aas
or AL,30H

;terge registrul AH
;AL = 33H
;AL = 33H 38H = FBH
;AX = FF05H
;AX = FF35H

n acest ultim caz, rezultatul din AL indic magnitudinea. Instruciunea


AAS seteaz indicatorul CF pentru a indica faptul c a fost generat un mprumut.
Dac privim n contextul scderii numerelor cu mai muli digii, rezultatul
final, FF35H, este util i corect. De exemplu, dac scdem 28 din 43 (de ex., 43
28), prima iteraie a buclei scade 3 8; obinem n AL rezultatul 5 i avem CF
setat. Urmtoarea iteraie, 4 2, folosete instruciunea SBB, care include n
operaie i mprumutul generat de scderea precedent; obinem 1. Aadar, dup
operaia SAU vom avea 31 35H, valoare care reprezint rspunsul corect (35).
Modulul de scdere al programului nostru arat astfel:
;; modulul de scdere
mov al,[esi]

sub al,[edi]
aas
or al,30h
mov [rez],al
jmp .end
Atenie, deoarece ar fi complicat programul, modulul de scdere nu ia n
considerare rezultatul negativ. Scopul nostru este s nelegem instruciunea AAS.
nmulirea ASCII
Instruciunea de corecie a rezultatului unei operaii de nmulire este AAM
(ASCII Adjust after Multiplication). Foarte important, spre deosebire de adunare i
scdere, nmulirea nu trebuie efectuat cu caractere ASCII, ci cu numere n format
BCD necompactat. Instruciunea AAM mparte valoarea din AL cu 10 i pstreaz
ctul n AH i restul n AL.
mov al,3
mov bl,8
mul bl
aam
or ax,3030H

;operand n format BCD necompactat (8 bii)


;operand n format BCD necompactat (8 bii)
;AX = 0018H (24 n zecimal)
;AX = 0204H
;AX = 3234H

Observai c operaia de nmulire folosete numere n format BCD


necompactat (nu numere ASCII). Dac digiii din AL i BL sunt n format ASCII,
trebuie s mascm digitul superior. De aceea, codul modulului de nmulire pentru
programul calc.asm arat astfel:
.L2:
mov dl,'*'
cmp dl,byte [ecx]
jne .L3
;; modulul de inmultire
mov al,[esi]
mov bl,[edi]
and al,0fh
and bl,0fh
mul bl
aam
or ax,3030h
cmp ah,30h
jne .L2_1

mov [rez],al
jmp .end
.L2_1:
xchg ah,al
mov [rez],ax
jmp .end
Deoarece pentru interpretorul de comenzi bash semnul stelu este caracter
special, cnd efectuai o nmulire, argumentul de semn din linia de comand
trebuie precedat de caracterul \ (ESCAPE), asfel:
./calc 3 \* 5
mprirea ASCII
Instruciunea AAD (ASCII Adjust before Division) corecteaz valoarea
dempritului din acumulator naintea mpririi a dou numere n format BCD
necompactat. Instruciunea AAD nmulete AH cu 10, l adun la AL (aceti doi
pai convertesc numrul din BCD necompactat n binar) i apoi l iniializeaz cu
zero. De exemplu:
mov ax,0205H
mov bl,08H
aad
div bl

;dempritul n format BCD necompactat


;mpritorul n format BCD necompactat
;AX = 0019H
;AX = 0103H

Instruciunea AAD convertete numrul stocat AX din BCD necompactat n


format binar, astfel nct s poat fi folosit instruciunea DIV. DIV genereaz
ctul n registrul AL i restul n registrul AH .
Modulul de mprire prezentat mai jos exemplific strictul necesar:
conversia din ASCII n BCD necompactat, i efectul instruciunii AAD. mparte o
cifr mai mare la una mai mic i afieaz rezultatul n form brut (coninutul
registrului AX).
.L3:
mov dl,'/'
cmp dl,byte [ecx]
jne .end
;; modulul de impartire
mov al,[esi]
sub al,30h
mov bl,[edi]

sub bl,30h
aad
div bl
or ax,3030h
mov [rez],ax
jmp .end
La fel ca la nmulire, cnd specificm argumentul de semn trebuie s
folosim caracterul ESCAPE.

10.3.3.

Erori i corecii n BCD compactat

Numerele n format BCD compactat pot fi adunate sau sczute cu ajutorul


a dou instruciuni dedicate:
DAA (Decimal Adjust after Addition)
DAS (Decimal Adjust after Substraction)
Nu exist suport pentru nmulire sau mprire. Pentru aceste operaii,
numerele trebuie convertite n format BCD necompactat.
Instruciunile DAA i DAS corecteaz rezultatul unei operaii de adunare,
respectiv de scdere, n conformitate cu formatul BCD compactat. Pentru a nelege
tipurile de corecii necesare, studiem urmtoarele exemple:
29H = 0010 10001 +
49H = 0100 10001
72H = 0111 0010

17H = 0001 0111+


24H = 0010 0100
3BH = 0011 1011

42H = 0100 0010 +


71H = 0111 0001
B3H = 1011 0011

n primul caz, rezultatul 72 nu este corect. Rezultatul corect, 78, se obine


prin adunarea cu 6 a rezultatului obinut. Eroarea const n propagarea unui bit de
transport de la digitul inferior spre cel superior. Eroare semnalizat de indicatorul
AF.
n al doilea exemplu, rezultatul trebuia s fie 41. Deoarece digitul inferior
este mai mare ca 9, rezultatul corect se obine prin adunare cu 6.
n ultimul exemplu, eroarea const n depirea capacitii digitului
superior. Soluia este s adunm la rezultat valoarea 60H. mpreun cu bitul de
transport generat (CF) rezultatul efectiv este 113.
Un exemplu legat de operaiile de scdere este urmtorul:
52H = 0101 0010 24H = 0010 0100

2EH = 0010 1110


n concluzie, rezultatele operaiilor de adunare sau scdere care folosesc
numere n format BCD compactat pot fi afectate de erori. Acestea sunt indicate de
prezena codurilor inexistente n BCD, de setarea indicatorului de transport (CF)
sau de setarea indicatorului de transport la jumtate (AF). n funcie de caz,
corecia se face prin adunarea sau scderea rezultatului obinut cu unul din
numerele 06H, 60H sau 66H.
Instruciunea DAA efectueaz acest tip de corecii asupra rezultatului
operaiilor de adunare. Mai exact, DAA efectueaz urmtoarele aciuni:
dac digitul inferior din AL este mai mare ca 9, sau indicatorul de
transport la jumtate este setat, adun 6 la AL i seteaz AF;
dac digitul superior din AL este mai mare ca 9, sau indicatorul de
transport este setat, adun 60H la AL i seteaz CF.
Instruciunea DAS corecteaz rezultatul operaiilor de scdere. Mai exact:
dac digitul inferior din AL este mai mare ca 9, sau indicatorul de
transport la jumtate este setat, scade 6 din AL i seteaz AF;
dac digitul superior din AL este mai mare ca 9, sau indicatorul de
transport este setat, scade 60H din AL i seteaz CF.
n acest moment cunoatem trei reprezentri care pot fi folosite n
operaiile aritmetice: binar, ASCII i BCD. Utilizarea unei reprezentri n
detrimentul alteia depinde n mare msur de tipul aplicaiei. Procesarea numeric
n ASCII este indicat n cazul operaiilor simple, efectuate de aplicaii interactive,
care preiau date de la utilizator i/sau afieaz rezultate pe ecran. Aa cum tim,
sistemul de calcul schimb informaii cu utilizatorul numai n format ASCII.
Conversia datelor de intrare n format binar consum resurse suplimentare i crete
timpul de execuie. Procesarea numeric n BCD este mai lent dect procesarea
numeric n ASCII, dar, n schimb, este mai flexibil - aa cum a reieit i din
paginile anterioare. n plus, conversia ntre ASCII i BCD este foarte simpl.
Formatul BCD compactat este mai eficient dect ASCII din moment ce fiecare
octet poate stoca doi digii zecimali. Dar cam aici se ncheie lista avantajelor.
n mod normal, datele numerice sunt reprezentate n binar. Este limbajul
nativ al calculatorului. Intern, acesta efectueaz toate operaiile aritmetice i logice
numai cu date reprezentate n binar. Procesarea numeric n binar este mult mai
eficient n comparaie cu celelalte reprezentri. Cnd aplicaiile folosesc prelucrri
numerice semnificative, versiunea binar e singura opiune viabil. Pe de alt parte,
tot ce introducem de la tastatur sau afim la monitor este n format text. Iar
formatul text nseamn ASCII. De aceea se impune un proces de conversie. Datele
primite de la tastatur sunt convertite n binar, prelucrate intern, apoi reconvertite

n ASCII i afiate la ecran. Aceste translatri necesit un efort suplimentar din


partea programatorului, dar numerele sunt procesate mult mai eficient n form
binar i, n final, se ajunge la un ctig de performan.

10.3.4.

Conversia ASCII binar

Urmtorul program cere dou numere de la tastatur, calculeaz suma i


afieaz rezultatul.
;
;suma.asm
;
%include io.inc
section .data
msg_numar1 db
"Introduceti primul numar: ",0
msg_numar2 db
"Introduceti al doilea numar: ",0
msg_rez
db
"Suma este: ",0
msg_eroare db
"A aparut transport!",0
section .bss
numar1
resd 1
;stocheaza primul numar
numar2
resd 1
;stocheaza al doilea numar
rez
resd 1
;stocheaza rezultatul
section .text
global _start
_start:
nop
WriteStr msg_numar1
ReadInt [numar1]
WriteStr msg_numar2
ReadInt [numar2]
;; calculeaza suma
mov eax,[numar1]
add eax,[numar2]
mov [rez],eax
;; verifica daca a aparut transport
jno fara_transport
WriteStr msg_eroare
nwln
jmp sfarsit

;; afiseaza rezultatul
fara_transport:
WriteStr msg_rez
WriteInt [rez]
nwln
sfarsit:
Exit
Pe lng funciile declarate n io.inc, programul mai folosete dou:
f_ReadInt i f_WriteInt. Prima citete de la tastatur un ir de caractere, l
convertete n numr, i ntoarce rezultatul n registrul EAX. A doua preia un
numr din registrul EAX, l convertete n ir de caractere, i afieaz rezultatul la
monitor. Pentru citirea de la tastatur i afiarea la monitor sunt folosite procedurile
cunoscute f_ReadStr i f_WriteStr. Aadar, n paragrafele urmtoare ne
vom concentra atenia asupra conversiei caracter - valoare binar. Dar, mai nti s
introducem n io.inc macroinstruciunile care le corespund:
extern f_ReadInt
extern f_WriteInt
%macro

WriteInt 1
push
EAX
mov
EAX,%1
call
f_WriteInt
pop
EAX
%endmacro
%macro ReadInt 1
%ifnidni %1,EAX
push
EAX
call
f_ReadInt
mov
%1,EAX
pop
EAX
%else
call f_ReadLInt
%endif
%endmacro
%ifidn (if identity) este o directiv a asamblorului YASM care testeaz dac
textul introdus este identic cu cel declarat. Construcia %ifidn text1,text2
va determina asamblarea codului ce o urmeaz numai i numai dac text1 i

text2, dup expandarea liniei respective, sunt buci identice de text. Spaiile
albe nu sunt luate n considerare. %ifidni (if identity insensitive) este similar
lui %ifdni numai c nu este sensibil la litere mari sau mici. %ifnidn (if not
identity) este forma negativ a lui %ifidn. Evident, exist i varianta %ifnidni.
Toate fac parte din familia directivelor de asamblare condiionat (directive folosite
de preprocesorul YASM). Directivele de asamblare condiionat permit asamblarea
unor seciuni numai n cazurile n care sunt ndeplinite anumite condiii. Sintaxa
general este:
%if <condiie1>
;intruciuni asamblate numai n cazul ndeplinirii <condiie1>
%elif <condiie2>
;instruciuni asamblate numai n cazul nendeplinirii <condiie1> i
ndeplinirii <condiie2>
%else
;instruciuni asamblate dac nu este ndeplinit nicio condiie anterioar
%endif
Aa se explic i apariia %else i %endif din macroinstruciunea noastr.
Similar, sunt posibile i expresiile %elifidn, %elifnidn, %elifidni i
%elifnidni. Preprocesorul YASM implementeaz o gam larg de directive
foarte utile.21

10.3.5.

Conversia caracterelor ASCII n binar

Funcia f_ReadInt citete de la tastatur un ir de caractere ce reprezint


un numr ntreg cu semn i returneaz valoarea numeric n registrul EAX. Partea
esenial a funciei convertete o secven de digii de intrare dai n format ASCII
la echivalentul lor binar. Procesul de conversie implic nmulirea repetat cu 10.
Presupunem c trebuie s convertim irul de caractere '475' n echivalentul
su numeric 475. '475' nseamn 343735H. Valoarea numeric a fiecrei cifre n
parte se obine simplu, prin scdere cu 30H. n acest caz, algoritmul este:
Digit de intrare Valoare numeric
Valoare iniial
'4' (34H)
4
'7' (37H)
7
'5' (35H)
5
21

Numr = numr 10 + valoare numeric


0
0 10 + 4 = 4
4 10 + 7 = 47
47 10 + 5 = 475

http://www.nasm.us/doc/nasmdoc4.html

Deoarece numrul este reprezentat pe 32 de bii, plaja de valori este


cuprins ntre 2.147.483.648 i +2.147.483.647. Dac introducem valorile
maxime, pe lng caracterul de semn vom avea nc 10 coduri numerice. Un total
de 11. Deoarece funcia f_ReadStr adaug la sfrit un caracter NULL, numrul
maxim de caractere valid este 12. Algoritmul prezentat, tradus n linii de
instruciuni, arat astfel:
.citeste_caracter:
;;furnizm funciei f_ReadStr parametri de intrare: registrul ESI specific
numrul de caractere presupus a fi citit de la tastatur, iar EDI adresa buffer-ului la
care vor fi stocate caracterele.
mov esi,0x10
;buffer de 16 caractere. Acesta poate fi
orict de mare, dar, din moment ce numrul maxim va avea 12 caractere, 16 este
acoperitor.
mov edi,buffer
call f_ReadStr
mov esi,buffer
;ncarc adresa bufferului n ESI
.converteste_caracter:
mov cl,byte [esi]
;ncarc caracterul n CL
cmp cl,0x0
;NULL?
je .sfarsit_conversie ;dac da, irul de caractere s-a sfrit
cmp al,0x30
;comparam cu '0'
jb .caracter_invalid ;caracter < '0' => nu este caracter numeric
cmp al,0x39
;comparam cu '9'
ja .caracter_invalid ;caracter > '9' => nu este caracter numeric
sub cl,0x30
;convertim ASCII la numeric
mul ebx
;nmulim EAX cu 10
jb .depasire_nr_caractere ;apariia unui transport nseamn
depirea domeniului de reprezentare. Se afieaz mesaj de depire.
add eax,ecx
;adunm digitul curent
jb .depasire_nr_caractere ;apariia unui transport n urma
operaiei de adunare nseamn depire.
cmp eax,0x80000000
;compar cu valoarea maxim
ja .depasire_nr_caractere ;dac EAX > 2.147.483.648,
numrul depete plaja de valori
inc esi
;urmtorul caracter
;; reintr n bucla de conversie. Din bucl se iese atunci cnd se ajunge la caracterul
NULL.
jmp .converteste_caracter

.sfarsit_conversie:
Acesta este algoritmul de conversie propriu-zis. Observai c verificm
validitatea caracterelor din buffer. Un lucru la care nu ne-am gndit nainte. Dar, la
o privire mai atent, secvena are totui o problem. De fapt, mai multe.
Utilizatorul poate introduce numere negative. n definitiv, putem aduna 30 cu -20.
n acest caz, irul de caractere '20' va fi precedat de caracterul '-'. Sau, dintr-un
exces de precizie, utilizatorul poate specifica explicit semnul numerelor pozitive
(de ex, +30) sau poate introduce spaii albe naintea caracterelor numerice. Aceste
posibiliti trebuie adresate. n plus, v aducei aminte c numrul are maxim 12
caractere. Un numr cu mai mult de 12 caractere nu poate fi reprezentat, aadar
este invalid. Secvena urmtoare de instruciuni trebuie s apar n cod imediat
dup introducerea caracterelor n buffer.
.sarim_peste_spatii:
inc esi
cmp byte [esi],0x20
je .sarim_peste_spatii
urmtorul caracter

;urmtorul caracter
;Spaiu?
;dac da, ignor-l i treci la

Bucla .sarim_peste_caractere este ntrerupt de primul caracter


diferit de caracterul de spaiu. Din acest moment tim c irul nu este precedat de
spaii.
.sir_fara_spatii:
;;Numrul reprezentat pe 32 de bii are maxim 12 caractere (caracter de semn, 10
caractere numerice, caracter NULL).
mov ecx,0xc
;contorizeaz nr. de caractere prelucrate
mov al,byte [esi]
;ncarc n AL primul caracter
cmp al,0x2b
;comparam cu semnul +
je .caracter_valid ;dac este +, atunci caracter valid
cmp al,0x2d
;comparam cu semnul je .caracter_valid
.caracter_valid:
inc esi
;urmtorul caracter
dec ecx
;decrementm ECX
jcxz .depasire_nr_caractere
.caracter_invalid:
Dac ECX = 0 i nu am terminat de parcurs irul, nseamn c numrul
este prea mare pentru a putea fi reprezentat pe 32 de bii. Condiia a fost testat cu

JCXZ. Deoarece instruciunea DEC nu seteaz indicatorii de stare, nu putem folosi


JZ.
.depasire_nr_caractere:
push ecx
; salveaz ECX n stiv
;;ncarc n ECX adresa mesajului de depire, afiat cu f_WriteStr
mov ecx,mesaj_depasire
call f_WriteStr
pop ecx
;extrage ECX
jmp .citeste_caracter ;reia procesul de citire
Dac primul caracter este invalid, semnalm evenimentul i relum
procesul de citire.
.niciun_caracter_valid:
push ecx
mov ecx,mesaj_lipsa_numar
call f_WriteStr
pop ecx
jmp .citeste_caracter ;reia procesul de citire
n acest moment putem nelege listingul integral. n plus, parcurgem
procedura pentru cazul n care am introdus numrul -475 precedat de dou
caractere de spaiu.
;
;f_ReadInt.asm
;
section .data
mesaj_depasire
db "Depasire domeniu de reprezentare",0
mesaj_lipsa_numar
db "Nu ati introdus niciun numar!",0
section .bss
buffer resb 256
section .test
extern f_ReadStr
extern f_WriteStr
global f_ReadInt
f_ReadInt:
push ebx
push ecx

push edx
push esi
push edi
pushf
.citeste_caracter:
mov esi,0x10
mov edi,buffer
call f_ReadStr
;;deoarece am introdus dou spaii i '-''4''7''5', bufferul conine 7 caractere, ultimul
este caracterul NULL, introdus de funcia de citire.
;; 0x20 0x20 0x2d 0x34 0x37 0x35 NULL
mov esi,buffer
;ncarc adresa bufferului n ESI
;; 0x20 0x20 0x2d 0x34 0x37 0x35 NULL
;;
;;
;;ESI
dec esi
.sarim_peste_spatii:
inc esi
cmp byte [esi],0x20
je .sarim_peste_spatii
;; 0x20 0x20 0x2d 0x34 0x37 0x35 NULL
;;
;;
;;
ESI
mov edi,esi
;salvam adresa de nceput n EDI
.sir_fara_spatii:
mov ecx,0xc
;; 0x20 0x20 0x2d 0x34 0x37 0x35 NULL
;;
;;
;;ECX = 12 ESI = EDI
mov al,byte [esi]
cmp al,'+'
je .caracter_valid
cmp al,'-'
je .caracter_valid
.testeaza_caracter:
cmp al,0x30
jb .caracter_invalid
cmp al,0x39
ja .caracter_invalid

.caracter_valid:
inc esi
dec ecx
;; 0x20 0x20 0x2d 0x34 0x37 0x35 NULL
;;
;;
;;ECX = 11 EDI ESI
jcxz .depasire_nr_caractere
mov al,byte [esi]
;ncarc n AL urmtorul caracter
jmp .testeaza_caracter
;;napoi la eticheta .testeaz_caracter. Din aceast bucl se iese cnd
numrul de caractere depete 12 sau cnd se ajunge la caracterul NULL. Aadar,
porile de ieire din bucl sunt reprezentate de etichetele .depasire_nr_
caractere sau .caracter_invalid.
.depasire_nr_caractere:
push ecx
mov ecx,mesaj_depasire
call f_WriteStr
pop ecx
jmp .citeste_caracter
.niciun_caracter_valid:
push ecx
mov ecx,mesaj_lipsa_numar
f_WriteStr
pop ecx
jmp .citeste_caracter
;sri la nceput
;;la primul caracter invalid se ajunge aici. Primul caracter invalid poate fi caracterul
NULL, aa cum ne ateptm, sau poate fi orice alt caracter (de ex., utilizatorul
introduce 687a). De aceea, prima instruciune din bucl are grij s introduc
caracterul NULL pe poziia primului caracter invalid.
;;n cazul nostru, se iese din bucl cnd ajungem la caracterul NULL.
;; 0x20 0x20 0x2d 0x34 0x37 0x35 NULL
;;
;;
;;ECX = 8 EDI
ESI
.caracter_invalid:
mov byte [esi],0x0
;introduce caracterul NULL
cmp ecx,0xc
;dac ECX = 12, nseamn c este

vorba de primul caracter. Dac primul caracter este invalid, afim mesaj.
je .niciun_caracter_valid
mov esi,edi
;dac ECX != 12, avem un numr valid. n
acest caz, reintroducem adresa primului caracter n ESI i pregtim registrele
pentru conversia ASCII - binar.
xor eax,eax
;EAX = 0
xor ecx,ecx
;ECX = 0
mov ebx,0xa
;EBX conine multiplicatorul
;; 0x20 0x20 0x2d 0x34 0x37 0x35 NULL
;;
;;
;;ECX = 0 ESI = EDI
mov cl,byte [esi]
;ncarc n CL primul caracter
cmp cl,'-'
;semnul - ?
je .sari_semn
;da, sri peste caracterul de semn
cmp cl,'+'
;semnul + ?
jne .converteste_caracter ;nu este caracter de semn; sri la
bucla de conversie
.sari_semn:
inc esi
;sri peste semn
;; 0x20 0x20 0x2d 0x34 0x37 0x35 NULL
;;
;;
;;CL = 0x2d EDI ESI
;;conversia ASCII - binar
.converteste_caracter:
mov cl,byte [esi]
cmp cl,0x0
je .sfarsit_conversie
sub cl,0x30
mul ebx
jb .depasire_nr_caractere
add eax,ecx
jb .depasire_nr_caractere
cmp eax,0x80000000
ja .depasire_nr_caractere
inc esi
;urmtorul caracter
jmp .converteste_caracter
;;n acest moment numrul este n registrul EAX
;; 0x20 0x20 0x2d 0x34 0x37 0x35 NULL
;;

;;
;;CL = NULL EDI
ESI
.sfarsit_conversie:
mov esi,edi
;ncarc n ESI adresa de nceput a irului
mov cl,byte [esi]
;ncarc n CL primul caracter
;; 0x20 0x20 0x2d 0x34 0x37 0x35 NULL
;;
;;
;;CL = 0x2d ESI = EDI
;;verificm dac EDX este zero. Dac este diferit de zero nseamn c numrul a
depit domeniul de reprezentare.
cmp edx,0x0
jne .depasire_nr_caractere ;
cmp eax,0x80000000
jb .numar_OK
;nr. < 2.147.483.648, valid.
cmp cl,0x2d
;negativ?
jne .depasire_nr_caractere ;Pozitiv. Numrul pozitiv nu
trebuie s fie mai mare de 2.147.483.647. Cum aici s-a ajuns deoarece nr. >
2.147.483.648, nseamn c a fost depit plaja de valori.
.numar_OK:
;numr < 2.147.483.648.
cmp cl,0x2d
;negativ?
jne .sfarsit
;pozitiv => utilizatorul a introdus un numr
pozitiv, fr semn explicit.
neg eax
;negativ => calculm complementul fa de doi.
.sfarsit:
popf
pop edi
pop esi
pop edx
pop ecx
pop ebx
ret

10.3.6.

Conversia numerelor din binar n ASCII

Procedura f_WriteInt afieaz pe ecran un ntreg de 32 de bii aflat n


registrul EAX. Funcia separ digiii individuali ai numrului i i convertete n
reprezentri ASCII. Separarea digiilor individuali se face prin mprire la 10.
Etapele implicate n procesul de conversie sunt exemplificate prin conversia
numrului 475.

475 / 10
47 / 10
4 / 10

Ct
47
4
0

Rest
5 + 30H
7 + 30H
4 + 30H

Valoare ASCII
35H
37H
34H

Apar cteva probleme:


Prima const n faptul c obinem digiii n ordine invers. n
consecin, acetia trebuie introdui n buffer n ordine invers ncepnd din partea dreapt (sfritul bufferului). Dar, mai nainte,
trebuie s introducem caracterul NULL, astfel nct procedura de
afiare s recunoasc finalul irului. Bufferul are 12 caractere:
caracterul de semn, 10 caractere numerice, caracterul NULL.
A doua problem este legat de semn. Dac numrul este negativ,
trebuie afiat semnul minus. Depistarea semnului se face prin
comparare cu zero. Dac valoarea din EAX este mai mic dect zero,
nseamn c numrul este negativ. n acest caz, trebuie s introducem
pe prima poziie din buffer caracterul '-' i s calculm complementul
fa de doi al numrului. Dac numrul este pozitiv, folosim caracterul
spaiu (afiarea obinuit a numerelor pozitive), iar valoarea rmne
neschimbat.
Prezentm procedura. Presupunem c n registrul EAX se afl valoarea
0xFFFFFF15 (numrul negativ -235).
;
;f_WriteInt.asm
;
section .bss
buffer resb 100
section .text
extern f_WriteStr
global f_WriteInt
f_WriteInt:
pusha
mov esi,buffer
;adresa de nceput a buffer-ului
mov byte [esi],0x20
;introduce caracterul de spaiu n primul
octet al buffer-ului
;;Buffer: 0x20 _ _ _ _ _ _ _ _ _ _ _ _
;;
;;

;;

ESI
cmp eax,0x0
;verificm semnul
jge .numar_pozitiv
;pozitiv
mov byte [esi],0x2d
;n caz c numrul este negativ rescrie
primul octet al bufferului cu semnul '-'. In acest mod, la sfarsit stim daca numarul
este pozitiv sau negativ.
neg eax
;calculeaza complementul fata de 2
;;Buffer: 0x2d _ _ _ _ _ _ _ _ _ _ _ _
;;
;;
;;
ESI, EAX = 0xEB (235)
.numar_pozitiv:
;numar pozitiv
mov ebx,0xa
;divizorul
add esi,0xb
;srim la adresa ultimului caracter (ESI +
11)
mov byte [esi],0x0
;introducem un caracter NULL
dec esi
;;Buffer: 0x2d _ _ _ _ _ _ _ _ _ _ _ NULL
;;
;;
;;EAX = 0xEB (235)
ESI
mov ecx,0xa
;contorizm
numrul
de
caractere
procesate
.converteste_numar:
mov edx,0x0
;EDX = 0
div ebx
;mprim EDX:EAX la EBX
add dl,0x30
;restul din DL se transform n caracter
mov byte [esi],dl
;se introduce pe prima pozitie din dreapta a
numarului
;;Buffer: 0x2d _ _ _ _ _ _ _ _ _ _ 0x35 NULL
;;
;;
;;EAX = 23
ESI
dec esi
;se decremeanteaza ESI
dec ecx
;ECX este contorul, numrul nu trebuie s
depeasc 11 cifre
cmp eax,0x0
;se compar EAX cu zero
;;Buffer: 0x2d _ _ _ _ _ _ _ _ _ _ 0x35 NULL
;;
;;

;;EAX = 23, ECX = 10


ESI
jne .converteste_numar
de conversie

;dac nu este egal reintrm n bucla

;;La ieirea din bucla de conversie:


;;Buffer: 0x2d _ _ _ _ _ _ _ _ 0x32 0x33 0x35 NULL
;;
;;
;;EAX = 0, ECX = 7
ESI
jcxz .afiseaza_numar ;dac EAX = 0 i ECX = 0 numrul este
format din toate cele 10 caractere.
;;Dac nu are 10 caractere, trebuie s copiem semnul. Acesta este i cazul nostru.
mov bl,byte [buffer] ;compar BL cu primul octet din buffer.
Acesta poate fi 0x20 pentru numr pozitiv i 0x2d pentru numr negativ
mov byte [esi],bl
;copiaz semnul la poziia curent
;;Buffer: 0x2d _ _ _ _ _ _ _ 0x2d 0x32 0x33 0x35 NULL
;;
;;
;;EAX = 0, ECX = 7
ESI
cmp bl,0x20
;dac semnul este 0x20 atunci este numr
pozitiv i nu trebuie s afim semnul.
jne .afiseaza_numar
;dac este negativ nseamn c putem afia
semnul.
inc esi
.afiseaza_numar:
mov ecx,esi
call f_WriteStr
popa
ret
Asamblm programul suma.asm i generm executabilul suma:
yasm -f elf -g stabs suma.asm
yasm -f elf -g stabs f_WriteStr.asm
yasm -f elf -g stabs f_ReadStr.asm
yasm -f elf -g stabs f_WriteInt.asm
yasm -f elf -g stabs f_ReadInt.asm
yasm -f elf -g stabs f_nwln.asm
ld -o suma suma.o f_WriteStr.o f_nwln.o f_ReadStr.o
f_ReadInt.o f_WriteInt.o melf_i386

10.4. Biblioteci de funcii


Modularizarea, ca metod de rezolvare a problemelor legate de creterea
dimensiunii programelor, introduce, la rndul ei, o nou problem, de data aceasta,
n procesul de generare a executabilului. Numrul mare de module complic
procesul de asamblare i editare de legturi, structura fiierului makefile, i
aglomereaz directorul ce conine fiierele surs i obiect. n momente precum cele
de la sfritul seciunii precedente, cnd a fost nevoie s executm un numr
apreciabil de comenzi pentru generarea unui singur executabil, ncepem s ne
gndim cum putem scrie toate modulele ntr-un singur fiier. Una din posibilitile
de rezolvare pe care le avem la dispoziie const n folosirea unor fiiere bibliotec,
sau simplu, biblioteci. O bibliotec este un fiier care conine un numr oarecare de
module obiect. n etapa de editare de legturi poate fi utilizat ca entitate de sine
stttoare. n mod obinuit, biblioteca este indexat, astfel nct simbolurile
coninute (funciile, datele, etc.) s poat fi gsite cu uurin; indexul indic
modulul care conine definiia unui anumit nume de simbol. Din acest motiv,
legarea unui program de module obiect aflate n biblioteci este mai rapid dect
legarea programului de fiiere obiect aflate pe disc. De asemenea, utilizarea
bibliotecilor presupune deschiderea unui singur fiier. Acest lucru crete i mai
mult viteza procesului de editare a legturilor. n Linux, la fel ca n toate sistemele
de operare moderne, dispunem de dou posibiliti prin care putem lega modulele
bibliotecilor de programele principale.
Prima metod se numete legare static (static linking). Legarea static
introduce modulul obiect al unei biblioteci direct n fiierul executabil. Indiferent
dac o funcie definit n acel modul obiect este utilizat sau nu, codul su va face
parte din executabil. Acest lucru poate crea executabile de mari dimensiuni i poate
duce la consum mare de memorie atunci cnd rulm n acelai timp mai multe
instane ale programului (fiecare instan are propria copie pentru aceeai funcie).
A doua metod se numete legare dinamic (dynamic linking). Legarea
dinamic permite programelor s adreseze funciile definite n biblioteci fr a
insera codul acestora n executabil. Bibliotecile legate dinamic pot fi partajate de
mai multe procese. Dac un proces nou are nevoie de simboluri definite de o
bibliotec aflat deja n memorie, poate folosi aceeai instan a bibliotecii.
Legarea dinamic permite consum de memorie redus i face posibil generarea
unor fiiere executabile de dimensiuni mai mici, salvnd spaiu pe disc.

10.4.1.

Crearea bibliotecilor statice

Bibliotecile statice sunt simple colecii de fiiere obiect obinuite (deoarece


sunt simple pachete de fiiere obiect, se mai utilizeaz i termenul de arhiv).

Aceast colecie este creat cu ajutorul programului ar (archiver). Prin convenie,


bibliotecile statice ncep cu lib i au extensia .a. Pentru a crea o bibliotec static
sau pentru a aduga un fiier obiect la o bibliotec static existent se folosete o
comand de genul:
ar -crs libioapi.a f_nwln.o f_WriteStr.o f_ReadStr.o
f_WriteChar.o f_ReadChar.o f_WriteInt.o f_ReadInt.o
unde opiunile:
-c creaz o arhiv,
-r insereaz n acea arhiv fiierele obiect specificate,
-s creaz un index pentru arhiv.
n exemplul de mai sus a fost creat o bibliotec static numit libioapi.a,
care conine copii ale fiierelor obiect prezente n comand. Evident, fiierele
obiect trebuie generate nainte printr-un proces de asamblare. Comanda ar
permite, pe lng alte opiuni descrise n paginile de manual, i afiarea modulelor
coninute de bibliotec:
ar t libioapi.a
f_nwln.o
f_WriteStr.o
f_ReadStr.o
f_WriteChar.o
f_ReadChar.o
f_WriteInt.o
f_ReadInt.o
Indexul poate fi listat cu ajutorul comenzii:
nm libioapi.a
Editarea legturilor cu programul se face prin opiunea -l. Aceasta
specific numele bibliotecii utilizate de program. De asemenea, se poate utiliza
opiunea L, care specific directorul n care se afl biblioteca ('.', se refer la
directorul curent).
ld o suma suma.o -L. -lioapi -melf_i386
Ordinea arhivelor n linia de comand este important. Pentru fiecare
arhiv ntlnit n linia de comand, asamblorul verific dac aceasta definete
vreun simbol cerut de fiierele obiect specificate naintea ei pe linia de comand.

Dac definete vreun simbol necesar, modulul obiect respectiv este copiat n
executabil. De aceea, bibliotecile trebuie specificate la sfritul liniei de comand22.

10.4.2.

Crearea bibliotecilor partajate

Bibliotecile partajate se obin printr-un proces asemntor celui descris


anterior: asamblarea unei colecii de fiiere obiect i nglobarea lor ntr-un fiier
partajat. Dar, spre deosebire de o bibliotec static - arhiv de fiiere obiect ce pot
fi copiate individual n executabil, n funcie de simbolurile necesare programului
principal (fiierele obiect care nu conin simboluri adresate de program nu sunt
copiate n corpul executabilului) -, o bibliotec partajat nu este o arhiv. Codul
modulelor obiect este practic uniformizat ntr-un singur fiier obiect. n consecin,
ncrcarea n memorie a bibliotecii partajate implic ncrcarea n totalitate a
codului definit n corpul su. Din faptul c o bibliotec partajat este un fiier
obiect rezult alt consecin important. Cnd generm fiiere obiect, nu
cunoatem adresa de memorie la care vor fi ncrcate. Pentru un fiier executabil,
segmentele de cod i date ncep la o adres virtual cunoscut dinainte. Codul
executabil nu este partajat i fiecare executabil primete propriul spaiu de adrese.
Acest lucru nseamn c editorul de legturi cunoate exact unde se va afla
seciunea de date n memorie i o va putea adresa direct. Bibliotecile nu cunosc
astfel de garanii. Seciunea lor de date se va afla la un anumit deplasament
specificat fa de adresa de baz; dar exact unde se afl aceast adres nu se poate
cunoate dect la lansarea n execuie. n concluzie, toate bibliotecile trebuie s
conin cod capabil de a fi executat indiferent de poziia sa n memorie, cunoscut
sub denumirea de cod independent de poziie (PIC Position Independent Code).
Reinei c seciunea de date se afl tot la un deplasament fix fa de seciunea de
cod, dar pentru a gsi adresa efectiv a datelor, deplasamentul trebuie adunat cu
adresa la care se ncarc biblioteca n memorie.
Din acest motiv, procesul de legare dinamic are loc n dou etape:
prima, la momentul editrii legturilor, editorul de legturi verific
faptul c toate simbolurile necesare programului se regsesc n
program sau n una din bibliotecile sale partajate. Simbolurile din
bibliotecile partajate nu sunt incluse n fiierul executabil final, ci
numai etichetate. Aadar, editorul de legturi las n urm referine
care trebuie completate cu informaii disponibile numai la momentul
ncrcrii n memorie (de ex., adresa unei funcii dintr-o bibliotec).
Aceste referine se numesc relocri.
n a doua etap, la lansarea n execuie a programului principal
(runtime), un alt program din sistem, numit editor de legturi dinamic
22

Specificai fiierul suma.o la sfritul comenzii ld i observai rezultatul.

(spre deosebire de cel anterior, denumit i editor de legturi static)


ncarc n memorie bibliotecile cu legare dinamic i completeaz
adresele referinelor (procesul de rezolvare a relocrilor).
Adresele la care editorul de legturi dinamic ncarc n memorie
bibliotecile partajate nu sunt cunoscute dinainte. Pur i simplu, pentru fiecare
bibliotec necesar programului, editorul dinamic gsete o zon de memorie
convenabil. Acest mecanism, mpreun cu cel de memorie virtual, face posibil ca
mai multe programe s foloseasc acelai cod. Avantajul major al bibliotecilor
partajate const n faptul c d posibilitatea mai multor programe s foloseasc
acelai cod (prin partajarea paginilor de memorie).
Tabelul deplasamentului global
Dar bibliotecile partajate pot conine la rndul lor relocri. Dac editorul de
legturi dinamic modific codul unei biblioteci partajate, conform unei relocri,
acel cod nu mai poate fi partajat, i pierdem avantajul utilizrii bibliotecilor
partajate. Imaginai-v c editorul de legturi dinamic trebuie s completeze n
seciunea de cod adresa unui simbol de date. Folosind mecanismul relocrilor,
editorul de legturi dinamic afl adresa de memorie a acestuia i rescrie codul
bibliotecii partajate. Din acest moment codul nu mai poate fi partajat. Pentru a
evita rescrierea codului, s-a stabilit ca editorul de legturi dinamic s rezerve o
zon din executabil special pentru memorarea adreselor de simboluri i s scrie
adresa acolo. n acest mod, codul nu trebuie modificat. Zona rezervat adreselor de
simbol se numete tabelul deplasamentului global (GOT Global Offset Table).
GOT este privat fiecrui proces i conine adresele absolute ale datelor private
(locale). Numai procesul care l deine are drepturi de scriere asupra lui.
Seciunea de date a unei biblioteci partajate nu are aceste restricii: din
moment ce seciunea de date are drepturi de scriere, ea trebuie copiat oricum n
memorie (mai degrab dect paginat) i att timp ct este copiat ea poate fi i
relocat.
Tabelul de cutare al procedurii
Bibliotecile partajate conin multe funcii, iar programul poate include
numai o parte din ele. Chiar i aa, n funcie de instruciunile de salt, programul
poate utiliza numai unele din cele incluse. Aa cum am vzut, procesul de legare
dinamic folosete resurse consistente, implic ncrcarea codului n memorie,
cutarea n mai multe tabele i scrierea adreselor. Orice mecanism care optimizeaz
acest proces duce la o cretere de performan. Tabelul de cutare a procedurii
(PLT Procedure Lookup Table) pune la dispoziie legarea ntrziat (lazy
binding). Legarea este sinonim cu procesul de rezolvare a relocrilor pentru datele
locale din GOT. Cnd se completeaz o intrare n PLT se spune c funcia a fost

legat de adresa real.


Procesul de legare a funciilor consum resurse la rndul su, de aceea,
acesta este ntrziat pn la momentul n care funcia este apelat. Fiecare funcie
din bibliotec are o intrare n PLT care, iniial, adreseaz un cod inactiv. Cnd
programul apeleaz funcia, acesta apeleaz de fapt o intrare n PLT (n acelai
mod ca datele referite prin GOT). Codul inactiv va transfera editorului dinamic un
numr de parametri cu ajutorul crora acesta afl adresa real a funciei. Adresa
real nlocuiete codul inactiv, astfel nct, la urmtorul apel, funcia este ncrcat
fr ajutorul editorului dinamic. Dac o funcie nu este utilizat, intrarea PLT nu va
fi niciodat modificat.
Suport YASM pentru codul independent de poziie
Codul independent de poziie presupune ca asamblorul s poat efectua
relocrile prezentate n seciunile anterioare. YASM le implementeaz prin
intermediul operatorului WRT. Totodat, pentru a obine tipurile de relocri
necesare, YASM definete cinci simboluri speciale. Aceste simboluri sunt utilizate
la dreapta operatorului WRT. Acetia sunt ..gotpc, ..gotoff, ..got, ..plt
i ..sym.
Obinerea adresei GOT
Pentru a putea fi gsit la ncrcare, GOT este situat la o distan constant
fa de seciunea de cod a bibliotecii partajate. Numele de simbol standard prin care
YASM se refer la GOT este _GLOBAL_OFFSET_TABLE_. Fiecare modul al
bibliotecii partajate trebuie s defineasc GOT ca simbol extern, astfel:
extern _GLOBAL_OFFSET_TABLE_
La nceputul fiecrei funcii care trebuie s acceseze seciunile DATA sau
BSS, trebuie nti s se calculeze adresa GOT. Acest lucru se face de obicei prin
scrierea funciei sub urmtoarea form:
func: push ebp
mov ebp,esp
push ebx
call .get_GOT
.get_GOT:
pop ebx
add ebx,_GLOBAL_OFFSET_TABLE_+$$-.get_GOT wrt ..gotpc
;corpul funciei

mov ebx,[ebp-4]
mov esp,ebp
pop ebp
ret
Primele dou linii ale funciei reprezint prologul C standard, destinat
crerii cadrului de stiv, iar ultimele trei linii formeaz epilogul C standard. A treia
i a patra linie de la sfrit salveaz i restaureaz registrul EBX, deoarece
bibliotecile partajate scrise n cod independent de poziie folosesc acest registru
pentru memorarea adresei GOT.
Partea important este format din instruciunea CALL i urmtoarele dou
linii. Combinaia CALL i POP obine adresa etichetei .get_GOT, fr a fi necesar
s cunotem dinainte adresa la care va fi ncrcat programul (instruciunea CALL
este codificat relativ la poziia curent). Instruciunea POP ncarc n registrul
EBX adresa etichetei .get_GOT (poziia curent). Instruciunea ADD utilizeaz
unul din tipurile speciale de relocare, GOTPC. Referirea la simbolul care marcheaz
nceputul GOT prin wrt ..gotpc se finalizeaz cu obinerea distanei ntre
cmpul operand al instruciunii ADD i adresa GOT. De aceea este necesar s
adaugm la rezultat adresa de nceput a seciunii curente, reprezentat prin dou
simboluri $$, i s scdem distana ntre adresa seciunii curente i adresa
.get_GOT, aflat deja n registrul EBX. La sfritul instruciunii ADD, EBX
conine adresa de nceput a GOT.
Modificm codul funciei f_nwln.asm astfel nct s devin independent
de poziie.
;
;funcia f_nwln , PIC
;
extern _GLOBAL_OFFSET_TABLE_
section .data
new_line: db
0xa
section .text
global f_nwln:function
f_nwln:
push ebp
mov ebp,esp
push ebx
call .get_GOT
.get_GOT:
pop ebx

add ebx,_GLOBAL_OFFSET_TABLE_+$$-.get_GOT wrt ..gotpc


lea esi,[ebx + new_line wrt ..gotoff]
mov eax,4
mov ebx,1
mov ecx,esi
mov edx,1
int 80h
mov ebx,[ebp-4]
mov esp,ebp
pop ebp
ret
Adresa GOT poate fi folosit la obinerea adresei elementelor de date.
Majoritatea acestora se vor afla n seciunile declarate local; ele pot fi accesate prin
intermediul tipului special ..gotoff. Instruciunea arat astfel:
lea esi,[ebx + new_line wrt ..gotoff]
Expresia new_line wrt ..gotoff este evaluat la momentul editrii
legturilor dinamice ca fiind distana de la nceputul GOT la variabila local
new_line. Adunarea cu adresa GOT din EBX va rezulta n ESI adresa locaiei
new_line.
Dac declarm variabile de tip global fr s le asociem o dimensiune,
ele sunt partajate ntre modulele bibliotecii, dar nu sunt exportate ctre programul
care a ncrcat biblioteca. Acestea se vor afla n seciunile DATA i BSS ca de
obicei, astfel nct s le putem accesa n acelai mod precum variabilele locale,
folosind mecanismul ..gotoff.
Adresarea elementelor de date externe i comune
Dac biblioteca trebuie s adreseze o variabil extern (extern bibliotecii,
nu doar a unuia din modulele sale), folosim tipul special ..got. Tipul ..got, n
loc s furnizeze deplasamentul dintre adresa de baz GOT i elementul de date,
returneaz deplasamentul dintre adresa de baz GOT i o nregistrare (intrare) GOT
care conine adresa elementului de date. Editorul de legturi static va configura
aceast intrare GOT la crearea bibliotecii, iar la momentul ncrcrii n memorie
editorul de legturi dinamic va plasa n ea adresa corect. Aadar, pentru a obine
n EAX adresa unui element de date extern new_line, trebuie s folosim
instruciunea:
mov eax,[ebx + new_line wrt ..got]

Aceasta ncarc adresa new_line ntr-o intrare din GOT. Editorul static,
atunci cnd creaz biblioteca partajat, colecteaz mpreun toate relocrile de tip
..got i construiete GOT astfel nct s se asigure c acesta prezint toate
intrrile necesare.
Elementele de date de tip common sunt adresate n acelai mod. Directiva
common se folosete la declararea elementelor de date comune. Un element de
date comun este asemntor unuia global, numai c este declarat n seciunea
datelor neiniializate.
common

data 4

este similar cu:


global data
section .bss
data resd 1
Diferena apare atunci cnd elementul de date comun este definit n mai
mult de un singur modul. Atunci, editorul de legturi static mbin aceste elemente
de date, iar referinele ctre elementul de date respectiv, din toate modulele, vor fi
direcionate ctre aceeai zon de memorie. La fel ca directivele extern i
global, directiva common poate primi extensii specifice formatului de
executabil:
common

vector 100:4 ;aliniere la 4 octei

Exportul simbolurilor ctre utilizatorul bibliotecii


Dac intenionm s exportm simboluri ctre utilizatorul bibliotecii,
trebuie s declarm dac acestea sunt elemente de date sau funcii, iar dac sunt
date, trebuie specificat dimensiunea acestora. Acest lucru este necesar deoarece
editorul dinamic trebuie s creeze intrri PLT pentru orice funcie exportat i, de
asemenea, trebuie s mute datele exportate n afara seciunii de date a bibliotecii
care le declar. Astfel, pentru exportul funciilor trebuie s folosim:
global func:function ;declar func ca funcie
func: push ebp
;etc.
Iar pentru exportul elementelor de date, de exemplu, un vector:

global vector:data vector.end-vector ;se specific mrimea


vector: resd 128
.end
Atenie, dac exportm un element de date ctre utilizatorul bibliotecii prin
declararea acestuia ca global i specificarea dimensiunii, elementul de date
respectiv va fi mutat n seciunea de date a programului principal. Acesta nu se va
mai afla n seciunea de date n care este declarat. Aadar, trebuie s adresm
propria variabil global cu mecanismul ..got, ca i cum ar fi un element de date
extern (ceea ce efectiv a i devenit), nu cu ..gotoff.
n mod asemntor, dac trebuie s stocm adresa unui element global,
exportat de ctre alt modul, n una din seciunile de date curente, nu putem folosi
declaraia standard:
data: dd

element_de_date_global

;GREIT

YASM va interpreta acest cod ca fiind o relocare obinuit, caz n care


elementele de date globale se regsesc la un deplasament judecat n funcie de
nceputul seciunii curente. Aadar, aceast referin indic seciunea de date
curent, nu elementul global (acesta se afl n alt parte). n loc de codul de mai
sus, scriem:
data: dd

element_de_date_global wrt ..sym

..sym provoac YASM s caute n tabela de simboluri un anumit element aflat la


adresa respectiv.
Metodele anterioare au aceeai semnificaie i pentru funcii: adresarea
unei funcii prin
funcptr:
dd
function
va furniza adresa codului scris n seciunea curent, n timp ce
funcptr: dd

function wrt ..sym

va furniza adresa PLT pentru funcia respectiv, acolo unde programul apelant
crede c se afl aceasta.
Apelul funciilor din exteriorul bibliotecilor
Apelarea funciilor din afara bibliotecii partajate trebuie s se realizeze prin
intermediul unui PLT. Fa de adresa la care este ncrcat biblioteca n memorie

PLT este plasat la un deplasament cunoscut, astfel nct codul bibliotecii s poat
efectua apeluri PLT ntr-un mod independent de poziie. PLT conine instruciuni
de salt ctre deplasamentele coninute de GOT, astfel nct apelurile de funcii
ctre alte biblioteci partajate sau rutine din programul principal pot fi transmise
transparent ctre destinaiile lor reale.
Pentru a apela o rutin extern trebuie s folosit un alt tip special de
relocare PIC, wrt ..plt. Aceasta este mai simpl dect variantele care lucreaz
cu GOT: pur i simplu se nlocuiete CALL f_nwln cu versiunea relativ la PLT,
CALL f_nwln wrt ..plt.

10.4.3.

Procesul de generare a bibliotecilor partajate

Urmtoarea comand asambleaz funcia f_nwln.asm n format PIC


dat mai sus:
yasm f elf g stabs f_nwln.asm
Se obine fiierul obiect f_nwln.o, transformat n bibliotec partajat cu
comanda:
ld -shared -o libioapi.so f_nwln.o -melf_i386
Numele unei biblioteci partajate trebuie prefixat de cuvntul lib i finalizat
cu .so. Opiunea -shared indic editorului de legturi faptul c trebuie s
genereze o bibliotec partajat, nu un executabil. n acest moment, n directorul
curent se afl biblioteca partajat libioapi.so, format dintr-un singur modul obiect.
Studiem biblioteca partajat folosind trei unelte foarte utile, file, nm i
objdump. Programul file specific tipul fiierului i ne permite s aflm pentru
ce platform a fost asamblat (compilat) acesta. Fiierul interogat poate fi un fiier
text, un executabil sau o bibliotec.
file libioapi.so
libioapi.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV),
dynamically linked, not stripped
nm enumer toate simbolurile care exist ntr-un obiect (prin obiect nelegem un
fiier obiect sau o bibliotec). Afieaz numele funciilor utilizate sau exportate de
ctre obiect, seciuni, simboluri, etc.. Biblioteca noastr partajat produce
urmtorul listing:
nm libioapi.so

00000175 t .n_so
00001178 a _DYNAMIC
000011f0 a _GLOBAL_OFFSET_TABLE_
000011fd A __bss_start
000011fd A _edata
00001200 A _end
000011fc d new_line
00000140 T f_nwln
0000014e t f_nwln.get_GOT
Dezasamblorul objdump ne permite s studiem modul n care operatorul wrt a
acionat asupra instruciunilor:
objdump -D -M intel libioapi.so
00000130 <f_nwln>:
130: 55
131: 89 e5
133: 53
134: e8 00 00 00 00

push
mov
push
call

ebp
ebp,esp
ebx
139 <f_nwln.get_GOT>

00000139 <f_nwln.get_GOT>:
139: 5b
pop ebx
13a: 81 c3 7f 10 00 00
add
ebx,0x107f
140: 8d b3 0c 00 00 00
lea
esi,[ebx+0xc]
146: b8 04 00 00 00
mov eax,0x4
14b: bb 01 00 00 00
mov ebx,0x1
150: 89 f1
mov ecx,esi
152: ba 01 00 00 00
mov edx,0x1
157: cd 80
int 0x80
159: 8b 5d fc
mov ebx,DWORD PTR [ebp-0x4]
15c: 89 ec
mov esp,ebp
15e: 5d
pop ebp
15f: c3
ret
000011b8 <.got.plt>:
11b8:
60
11b9:
11 00
...
Disassembly of section .data:

pusha
adc DWORD PTR [eax],eax

000011c4 <new_line>:
11c4:0a
.byte 0xa
n urma instruciunii LEA ESI,[EBX + 0xc], n registrul ESI se va
afla adresa new_line, 000011c4. Dac scdem din aceasta 0xc rezult adresa
000011b8, adic adresa de nceput a tabelei GOT.

10.4.4.

Instalarea i utilizarea bibliotecilor partajate

Urmtorul program folosete biblioteca partajat pentru a afia la ecran un


caracter de linie nou.
;
;prog.asm, PIC
;
section .text
extern f_nwln
global _start
_start:
nop
call f_nwln wrt ..plt
mov eax,1
mov ebx,0
int 80h
Asamblarea acestuia are loc n mod obinuit:
yasm -f elf -g stabs prog.asm
n schimb, deoarece folosete o funcie ce aparine unei biblioteci partajate, aceasta
nu va fi inclus n fiierul executabil final i este nevoie ca editorul de legturi
dinamic s ncarce n memorie biblioteca partajat i s rezolve relocrile. n
sistemele Linux editorul de legturi dinamic este ld-linux.so.2, prezent n directorul
/lib. Pentru a specifica programului principal c are nevoie de editorul dinamic
folosim parametrul -dynamic-linker.
ld -dynamic-linker /lib/ld-linux.so.2 -o prog prog.o L. -lioapi -melf_i386
Cnd rulm un program care depinde de o bibliotec partajat, sistemul va cuta

aceast bibliotec n cteva locuri standard: /lib i /usr/lib. Dac fiierul


.so nu poate fi gsit n niciuna din aceste locaii, executabilul nu va putea fi lansat
n execuie. Comanda ldd [prog] poate depista bibliotecile partajate de care
depinde programul i dac ele sunt pot fi gsite de sistem sau nu. Avem dou
opiuni:
copiem biblioteca partajat n unul din directoarele amintite, lucru care
nu este ntotdeauna posibil, deoarece sunt necesare drepturi privilegiate
(de root),
sau le folosim din cadrul unui alt director, posibil chiar directorul
curent. n acest caz, trebuie s configurm corespunztor variabila de
mediu LD_LIBRARY_PATH. De exemplu, dac biblioteca partajat se
afl n acelai director cu fiierul executabil, executm comanda:
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
Acum putem instala biblioteca partajat. Bibliotecile partajate sunt
instalate cu ajutorul unui program special, numit ldconfig. n mod normal, dac
bibliotecile se gsesc n directoarele standard, putem rula ldconfig pur i
simplu. Dar, pentru c trebuie s instalm o bibliotec aflat n directorul curent,
rulm:
ldconfig -v -n .
Programul se execut n mod obinuit:
./prog
ncheiem aici subiectul bibliotecilor partajate. Procesul este mult mai
complex i necesit divagaii care nu intr n planul acestei cri. Observai c nu
am transcris funciile noastre n cod independent de poziie. Metoda GOT este
destul de ineficient din punct de vedere al performanelor: codul trebuie s extrag
o adres din GOT de fiecare dat cnd citete sau scrie n segmentul de date (risip
de timp i registre - EBX are rol dedicat). Dei se poate folosi i alt metod mai
rapid, am ales s prezintm mecanismul standard. n plus, codul independet de
poziie este mai uor de implementat pe sistemele de 64 de bii.

10.5. Apelul funciilor de sistem prin biblioteca


standard C
Orice sistem de operare similar ca funcionalitate cu Unix conine o
interfa de apelare a funciilor de sistem. Aceasta definete apelurile funciilor de
sistem. n Linux, biblioteca C se numete glibc i conine funcii pentru toate
facilitile puse la dispoziie de sistemul de operare. n consecin, orice aplicaie
creat sau instalat de utilizator poate accesa kernelul prin interfaa de apelare a
funciilor de sistem sau prin intermediul glibc.

Aplicaie

Aplicaie
Spaiul utilizator
Aplicaie
Biblioteca C

Intefaa de apelare a funciilor de sistem


Spaiul kernel
Kernel

Drivere de dispozitiv

Hardware

Figura 10.4 Relaia ntre aplicaii, kernel i hardware

Aceast seciune descrie modul n care putem utiliza funcii C n


programele scrise n limbaj de asamblare. Bibliotecile C conin toate funciile
standard utilizate n programele C, precum cea de afiare a irurilor de caractere
(printf) sau funcia de ncheiere a proceselor (exit).

Urmtorul program afieaz propriile argumente primite din linia de


comand.
;
;args.asm
;
section .data
nrArgs
db "Sunt %d argumente: ",10,0
Args db "%s",10,0
section .text
extern printf
extern exit
global _start
_start:
nop
mov ecx,[esp]
push ecx
push nrArgs
call printf
add esp,4
pop ecx
mov ebp,esp
add ebp,4
bucla:
push ecx
push dword [ebp]
push Args
call printf
add esp,8
pop ecx
add ebp,4
loop bucla
push 0
call exit
Observai c funciile printf i exit sunt declarate cu directiva
extern, la fel ca orice funcie definit ntr-un fiier extern.
Dac ai lucrat n C, funcia printf v este cunoscut. Ne permite s
tiprim la ieirea standard att text simplu fr nicio formatare,
Argumentele sunt urmtoarele:
ct i propoziii complicate, formate din cuvinte obinuite, cifre sau caractere de
conversie a datelor numerice:

Numrul de argumente este %d:


Sintaxa funciei printf este urmtoarea:
printf Text FORMATAT, arg1, arg2 ...
unde textul formatat conine caractere obinuite, copiate explicit la ieire,
dar i coduri de control care determin modul de conversie i tiprirea
argumentelor. Fiecare specificaie de formatare este introdus prin caracterul % i
ncheiat de un caracter de conversie. Cteva din codurile de conversie sunt listate
n Tabelul 10.3.
Tabelul 10.4 Coduri de conversie pentru funcia printf

Cod de conversie
%d
%u
%x
%c
%s
%%

Descriere
Argumentul este convertit n zecimal
Argumentul este convertit n zecimal fr semn
Argumentul este convertit n hexazecimal
Argumentul este considerat ca fiind un singur caracter
Argumentul este un ir de caractere
Afieaz simbolul procent

ntre % i caracterul de conversie pot fi:


un semn (minus), care semnific alinierea la stnga a argumentului
convertit;
un ntreg, care specific lungimea minim a irului. Argumentul va fi
aliniat la dreapta i va fi completat cu spaii albe pn la lungimea
cmpului.
Caracterele din cadrul argumentului corespunztor formatului %s sunt
preluate pn la ntlnirea caracterului NULL. Acesta este i motivul pentru care n
program am ataat un zero la irurile destinate mesajelor:
"Sunt %d argumente: ",10,0
Numrul 10 reprezint caracterul ASCII \n.
Mecanismul de formatare este simplu, problema real const n
transmiterea argumentelor. Funcia printf nu permite specificarea numrului de
argumente. Numrul acestora variaz n funcie de necesitile programatorului. n
schimb, toi parametrii primii de funciile bibliotecii C trebuie transmii prin
intermediul stivei, n concordan cu convenia C. Acest lucru se face direct, prin

introducerea valorii, sau indirect, prin referin (prin introducerea adresei de 32 de


bii a argumentului). Pentru elemente de date cu valori de 32 sau 64 de bii,
introducem valorile direct. Pentru elemente de date mai mari, precum iruri sau
vectori, introducem n stiv adresa de nceput (n jargonul C, transmiterea unei
adrese ctre ceva se numete transmiterea unui pointer ctre acel ceva).
Cnd funcia printf primete mai muli parametrii, acetia trebuie
introdui n stiv ntr-o ordine foarte exact: de la dreapta la stnga, aa cum ar fi
aprut n corpul instruciunii printf ntr-un program scris n C. Un exemplu
simplu din C este urmtorul:
printf(ntotdeauna %d + %d = %d, 2,3,5);
Aceasta este o propoziie n C care apeleaz funcia printf. irul de baz este
nchis ntre ghilimele i reprezint primul argument. irul este urmat de cteva
argumente numerice. Trebuie s fie o valoarea numeric pentru fiecare cod de
formatare %d gsit n ir. Ordinea n care aceste elemente trebuie introduse n stiv
ncepe cu elementul din dreapta i continu ctre stnga, irul de baz fiind ultimul.
n asamblare, secvena arat astfel:
push 5
push 3
push 2
push mesaj
call printf
add esp,16
Identificatorul mesaj este adresa irului de baz, aa cum este acesta
definit n segmentul de date.
Mesaj

db

ntotdeauna %d + %d = %d,0

Instruciunea de adunare de la final elibereaz stiva. Ne amintim c, de


fiecare dat cnd introducem ceva n stiv cu instruciunea PUSH, indicatorul de
stiv ESP se deplaseaz ctre adrese mai mici cu un numr de octei egal cu
dimensiunea elementelor. Patru argumente reprezint 16 octei. La sfritul
apelului indicatorul de stiv trebuie s conin adresa dinaintea acestuia, aadar,
adunm 16.

10.5.1.

Editarea legturilor cu funciile C

Cnd folosim n programele de asamblare funcii din biblioteca C trebuie


s legm fiierele bibliotecii de fiierul obiect al programului. Dac funciile
bibliotecii C nu sunt disponibile, editorul de legturi va afia mesaj de eroare.
ld -o args args.o
args.o: In function `_start':
args.asm:13: undefined reference to `printf'
args.o: In function `bucla':
args.asm:20: undefined reference to `printf'
args.asm:26: undefined reference to `exit'
Pentru a putea edita legturile cu funciile C, acestea trebuie s fie
disponibile n sistem. n sistemele Linux, biblioteca C standard dinamic se afl n
fiierul libc.so.x, unde x este o valoarea care reprezint versiunea bibliotecii.
Acest fiier bibliotec conine funciile standard C, incluznd printf i exit.
Fiierul libc.so trebuie legat explicit de codul obiect, folosind parametrul -l al
editorului de legturi GNU, ld. Cnd utilizm parametrul -l, nu trebuie s
specificm numele complet al bibliotecii. Editorul de legturi presupune c
biblioteca se va afla ntr-un fiier numit /lib/libx.so, unde x este numele
bibliotecii specificat ca parametru n linia de comand n acest caz, litera c. Pe de
alt parte, funciile nu vor fi incluse n fiierul executabil final i biblioteca
dinamic trebuie ncrcat n memorie la momentul lansrii n execuie a
programului, de un alt program. Pentru sistemele Linux, acest program este ldlinux.so.2, regsit n directorul /lib. Pentru a specifica programului nostru
c are nevoie de editorul de legturi dinamic, folosim parametrul -dynamiclinker.
ld -dynamic-linker /lib/ld-linux.so.2
args.o -verbose

-o args -lc

Asamblarea se face n mod obinuit, cu YASM. La rulare, pentru a gsi biblioteca


libc.so programul folosete ncrctorul dinamic ld-linux.so.2.
Putem testa programul args cu orice numr de parametrii.
./args test 10 20 30
Sunt 5 argumente:
./args
test
10

20
30
Amintii-v c primul parametru este considerat numele programului, aadar,
primul argument din linia de comand este al doilea parametru. O greeal ntlnit
frecvent const n compararea cu zero a numrului de argumente din linia de
comand. Din moment ce numele programului se va regsi ntotdeauna pe linia de
comand, numrul de parametri nu va fi niciodat zero.
Urmtorul program afieaz variabilele de mediu.
;
;env.asm
;
section .data
Args db "%s",10,0
section .text
extern printf
extern exit
global _start
_start:
nop
mov ebp,esp
add ebp,12
bucla:
cmp dword [ebp],0
je sfarsit
push dword [ebp]
push Args
call printf
add esp,12
add ebp,4
loop bucla
sfarsit:
push 0
call exit
./env
USERNAME=stefan
DEFAULTS_PATH=/usr/share/gconf/gnome.default.path
GIO_LAUNCHED_DESKTOP_FILE=/usr/share/applications/terminator.desktop
XDG_CONFIG_DIRS=/etc/xdg/xdg-gnome:/etc/xdg
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
DESKTOP_SESSION=gnome

PWD=/home/stefan
GDM_KEYBOARD_LAYOUT=us
LANG=en_US.UTF-8
GDM_LANG=en_US
MANDATORY_PATH=/usr/share/gconf/gnome.mandatory.path
UBUNTU_MENUPROXY=libappmenu.so
COMPIZ_CONFIG_PROFILE=ubuntu
GDMSESSION=gnome
SHLVL=1
HOME=/home/stefan
LANGUAGE=en_US:en
Cnd n linia de comand nu sunt specificai parametrii, seciunea variabilelor de
mediu ncepe la un deplasament de 12 octei fa de adresa stocat n registrul ESP.
Sfritul seciunii este marcat cu un caracter NULL. Compararea valorii din stiv
cu zero verific faptul c am ajuns la sfritul seciunii. Dac adresa nu este zero,
se afieaz irul indicat de aceasta. Variabilele de mediu prezente n sistem depind
de aplicaiile active i de setrile locale. Putem verifica acest lucru prin crearea
unei noi variabile.
export TEST=/home/test
./env | grep TEST
TEST=/home/test

11. INTERFAA CU LIMBAJELE DE NIVEL


NALT

n capitolul III am prezentat avantajele i dezavantajele limbajelor de nivel


nalt i de asamblare i, nc din prefa, am atenionat cititorul c scrierea
complet a programelor n asamblare trebuie evitat ct de mult posibil.
Programele n asamblare sunt mai lungi, mai dificil de actualizat i, mai ales, nu
sunt portabile. n schimb, ofer acces direct la hardware i pot reduce timpul de
execuie. Ca urmare, dac este necesar, programele de nivel nalt pot beneficia de
performana ridicat a unor funcii scrise n asamblare. n astfel de programe este
posibil ca o funcie de nivel nalt s apeleze o funcie de nivel sczut, sau invers. n
acest capitol prezentm diferite modaliti prin care putem introduce funcii scrise
n asamblare n corpul programelor de nivel nalt, n acest caz, limbajul C. Scopul
nostru este acela de a ilustra principiile implicate. Odat ce acestea sunt nelese,
discuia poate fi generalizat la orice alt limbaj de nivel nalt.
Cele dou metode prin care putem introduce secvene de instruciuni n
asamblare n codul C sunt:
scrierea instruciunilor de asamblare direct n codul C, sau
legarea de programul C a unor module scrise n asamblare, la
momentul compilrii. Proces asemntor celui de legare a programelor
C de funciile aflate n bibliotecile C standard.
Prima metod se numete inline assembly i este util numai atunci cnd
trebuie s includem n codul C o secven relativ scurt de cod n asamblare. n caz
contrar, este preferat metoda modulelor separate. n acest ultim caz, modulul scris
n asamblare devine o funcie pe care programul C o tratateaz la fel ca pe orice
alt funcie C. Dac sunt utilizate module separate, C i asamblare, fiecare modul
este translatat n fiierul obiect corespunztor: modulul C cu ajutorul unui
compilator, modulul n asamblare cu ajutorul unui asamblor. n procesul de
generare a fiierului executabil editorul de legturi utilizeaz ambele fiiere obiect.
S presupunem c programul nostru hibrid conine dou module: un modul C,
fiierul main.c, i un modul n asamblare, fiierul proc.asm. Procesul prin care se
obine fiierul executabil conine dou etape. n prima etap generm fiierul obiect
pentru modulul n asamblare:
yasm -f elf -g stabs proc.asm

Acest pas creaz fiierul proc.o. A doua etap compileaz modulul C i editeaz
legturile ntre cele dou module obiect:
gcc -g stabs -o program main.c proc.o m32
Editorul de legturi este invocat automat de ctre GCC.

11.1. Apelarea modulelor n asamblare din C


n capitolul intitulat Funcii am demonstrat cum trebuie s arate o funcie n
limbaj de asamblare astfel nct s poat fi utilizat de un program n asamblare.
Aceeai tehnic poate fi utilizat i pentru funciile n asamblare care vor fi
ncorporate n programe C. Tot n acel capitol am vzut c funciile pot primi n
diferite moduri parametrii de intrare, unul dintre ele fiind caracteristic programelor
C. Programele C folosesc o metod i un format specific, i anume, transferul
parametrilor prin intermediul stivei. n plus, unele registre au rol prestabilit, rol
care trebuie respectat cu strictee.
Tabelul 11.1 Rolul registrelor n convenia de apel C

Registru

Descriere
Destinat s pstreze valoarea returnat de funcie; poate fi modificat
EAX
pn la revenirea din funcie.
EBX
Destinat s indice tabelul global de deplasamente; trebuie pstrat
ECX
Disponibil pentru uzul intern al funciei
EDX
Disponibil pentru uzul intern al funciei
EBP
Indic adresa de baz a stivei; trebuie pstrat
ESP
Indic noua locaie a stivei n cadrul funciei; trebuie pstrat
EDI
Utilizat ca registru local de ctre programul C; trebuie pstrat
ESI
Utilizat ca registru local de ctre programul C; trebuie pstrat
Destinat s pstreze valoarea n virgul mobil returnat de funcie;
ST(0)
poate fi modificat pn la revenirea din funcie
ST(1) - ST(7) Disponibile pentru uzul intern al funciei

Regulile care trebuie respectate de orice funcie pentru a putea fi apelat dintr-un
program C formeaz aa numitele convenii de apel C. Toate funciile
implementate n bibliotecile C respect aceste reguli. Aceleai convenii trebuie
respectate cu strictee i de funciile scrise n asamblare. Pe scurt, acestea sunt:
Funcia trebuie s pstreze valorile iniiale ale registrelor EBX,
ESP, ESI, EDI. Funcia poate folosi aceste registre n corpul ei dar,

la revenirea din funcie, registrele menionate trebuie s conin


valorile dinainte de apelarea acesteia. Coninutul tuturor celorlalte
registre de uz general poate fi modificat dup dorin.
Rezultatul unei funcii este returnat n registrul EAX - n cazul unei
valori de 32 de bii sau mai mici -, n EDX:EAX - pentru o valoare
de 64 de bii -, sau n registrul ST(0) pentru valori n virgul
mobil. irurile, structurile sau alte elemente de date mai mari de
32 de bii sunt returnate prin referin (funcia returneaz n EAX
adresa de nceput a acestora).
Parametrii de intrare ai funciilor sunt introdui n stiv n ordine
invers fa de cea a apariiei lor n declaraia funciei.
Presupunnd c avem funcia Proc(alpha, beta, gamma),
primul parametru introdus n stiv trebuie s fie gamma, urmat de
beta i, ultimul, alpha.
Funciile nu extrag parametrii din stiv. Programul apelant
efectueaz acest lucru dup revenirea din funcie, fie prin
instruciunea POP, fie prin adunarea unui deplasament la
indicatorul de stiv ESP (metod ntlnit mai des, deoarece este
mai rapid).
Dei nu este o convenie explicit, urmtoarea cerin este
important: eticheta punctului de intrare n program pentru funcia
n asamblare trebuie s fie declarat global.

Respectarea conveniilor de apel C ncepe cu salvarea i refacerea


coninutului registrelor: introducerea lor n stiv, la nceputul funciei, i extragerea
lor din stiv, la revenirea n programul principal. Procesul are loc n secvenele
standard, prolog i epilog (la fel ca la funciile utilizate de programele n asamblare
studiate n capitolele precedente).

11.1.1.

Structura funciei n asamblare

Structura ablon a funciilor n asamblare destinate apelrii din interiorul


programelor C arat astfel:
;
;FuncTemplate.asm
;
section .data
section .bss

section .text
global proc

;punct de intrare necesar editorului de legturi

proc:
push ebp
;seteaz cadrul de stiv
mov ebp,esp
sub esp,12
push ebx
;funcia trebuie s pstreze EBP, EBX, ESI, & EDI
push esi
push edi
< codul funciei >
pop
pop
pop
mov
pop
ret

edi
esi
ebx
esp,ebp
ebp

;restaureaz registrele EBP, EBX, ESI & EDI


;elimin cadrul de stiv nainte de revenire
;revenire n program

Acest ablon trebuie respectat de toate funciile n asamblare ce vor fi


apelate din cadrul programelor C sau C++. Desigur, dac o funcie nu altereaz
coninutul registrelor EBX, ESI sau EDI, putem omite instruciunile PUSH i POP
echivalente. O mic remarc: din faptul c este necesar salvarea registrele EBX,
ESP, EBP, ESI i EDI reiese c toate celelalte registre de uz general pot fi rescrise.
i aici, mare atenie: pot fi rescrise de oricine, nu numai de noi. Dac din interiorul
funciei scrise de noi sunt apelate alte funcii (din bibliotecile standard, de
exemplu), acele funcii pot altera valorile din EAX, ECX i EDX. Acest lucru
nseamn c nu putem presupune faptul c o valoare contor aflat n ECX va
rmne nemodificat dup un apel al funciei C printf. Dac funcia scris de
noi utilizeaz ECX ca registru contor i totodat include apeluri ctre funcii
bibliotec sau ctre orice alt funcie care nu e scris de noi trebuie s salvm
explicit valoarea acestuia n stiv, nainte de apel, i s refacem explicit valoarea
acestuia, la revenirea din funcie. Acelai lucru este valabil i pentru EAX i EDX.
n cazul celui dinti, tim c funciile standard returneaz rezultatul n registrul
EAX, aadar, cnd apelm astfel de funcii, este evident c acesta va fi rescris.
Observai instruciunea SUB inclus n prolog. Aceasta rezerv n stiv
spaiu destinat stocrii variabilelor locale (utilizate n cadrul funciei noastre).
Instruciunea din ablon rezerv n stiv un numr de 12 octei de memorie. Acetia
pot stoca trei elemente de date a cte 4 octei fiecare. Dac este necesar mai mult
spaiu, se scade din ESP numrul de octei corespunztor. Variabilele locale sunt

adresate din interiorul funciei, relativ la registrul EBP. De exemplu, dac prima
variabil local ocup 4 octei, adresa locaiei va fi [EBP-4]. A doua variabil
local poate fi adresat cu [EBP-8], iar a treia cu [EBP-12].
Totui, funciile n asamblare pot declara propriile segmente .data i
.bss. n acest caz, la momentul compilrii, aceste zone de memorie vor fi
combinate cu zonele .data i .bss ale programului C.

11.1.2.

Cadrul de stiv

Pentru programele n asamblare, stiva este extrem de important. Pentru


programele C sau cele care interacioneaz cu C, stiva este esenial. n C, stiva are
un rol central. Motivul este simplu: compilatoarele sunt roboi care traduc codul C
n limbaj n asamblare. Acest lucru nseamn c folosesc metode mecanice, iar
majoritatea acestora depind enorm de existena stivei. n sistemele Linux,
mecanismele folosite de compilator se bazeaz pe noiunea de cadru de stiv.
Compilatoarele folosesc cadrele de stiv n procesul de alocare a spaiului pentru
variabile locale.
Un cadru de stiv reprezint o regiune (din stiv) marcat ca aparinnd
unei anumite funcii. n esen, aceasta este regiunea cuprins ntre adresele aflate
n dou registre: indicatorul de baz (EBP) i indicatorul de stiv (ESP).

Stiva

EBP+16:

Direcia de cretere a adreselor de memorie

EBP+12:
EBP+8:
EBP+4:
EBP+0:

EBP apelant

EBP-4:

EBX apelant

EBP-8:

ESI apelant

EBP-12:

EDI apelant

EBP-16:

(locaie temporar)

EBP-20:

(locaie temporar)

EBP-24:

(locaie temporar)

EBP-28:

(locaie temporar)

EBP-32:

(locaie temporar)

EBP-36:

(locaie temporar)

Datele din aceast zon


aparin urmtorului cadru
de stiv
EBP rmne fix pn cnd
cadrul de stiv este
EBP
eliminat.
EPB conine valoarea ESP
a funciei apelante.

Aceast regiune reprezint


cadrul de stiv curent

ESP

Memorie neutilizat
ESP se deplaseaz
n sus i jos

Figura 11.1 Un cadru de stiv

Cadrul de stiv este creat prin salvarea n stiv a registrului EBP (registrul
EBP al funciei apelante) i copierea indicatorului de stiv al funciei apelante n
registrul EBP. Odat ce EBP este ancorat la acel capt al cadrului de stiv,
indicatorul de stiv, ESP, se poate deplasa liber, n funcie de necesitile funciei.
Primele dou instruciuni din prolog:
push ebp
mov ebp,esp
n urma acestor dou instruciuni, registrul EBP este considerat ancora noului cadru
de stiv (sau indicator de cadru). Toate elementele aflate deasupra lui n stiv
(deasupra cadrului de stiv al funciei curente) nu pot fi adresate dect prin
intermediul acestuia. Acolo sunt i parametrii de intrare ai funciei, dac aceasta
necesit aa ceva. Un alt motiv pentru care EBP nu trebuie modificat este c
valoarea indicatorului de stiv al apelantului, ESP, se afl n EBP. Revenirea din
funcie cu un ESP modificat nseamn funcionarea defectuoas a programului

apelant.
nainte de finalul funciei, cadrul de stiv trebuie eliminat. Acesta este rolul
ultimelor dou instruciuni din epilog:
mov esp,ebp
pop ebp
n acest moment, cadrul de stiv nu mai exist i putem executa n
siguran instruciunea RET. Instruciunea RET red controlul programului C.

11.1.3.

Compilarea modulelor

Compilatorul GCC poate obine fiiere obiect din funcii scrise n limbaj
de asamblare i le poate aduga la corpul programului C. Fiierul obiect al funciei
n asamblare este generat cu ajutorul asamblorului YASM.
Urmtoarea funcie n asamblare afieaz un mesaj prin intermediul
funciei standard printf.
;
;proc.asm
;
section .data
Msg db "Mesaj din interiorul functiei in asamblare.",10,0
section .text
global proc
extern printf
proc:
push ebp
mov ebp,esp
sub esp,12
push ebx
push esi
push edi
push Msg
call printf
add esp,4
pop
pop
pop
mov

edi
esi
ebx
esp,ebp

pop ebp
ret
Funcia proc.asm folosete secvenele prolog i epilog specifice conveniei
de apel C. Observai cum numele su este declarat global i faptul c textul
mesajului este declarat n seciunea datelor iniializate. Pentru afiarea acestuia se
folosete funcia standard printf, declarat ca extern. Asamblm funcia n
asamblare:
yasm -f elf -g stabs proc.asm melf_i386
Odat creat fiierul obiect, acesta poate fi specificat n linia de comand a
compilatorului, alturi de fiierul C surs. Fiierul programului principal este numit
main.c.
/* programul principal apeleaz funcia n asamblare */
#include <stdio.h>
int main()
{
extern void proc();
proc();
printf("Mesaj din interiorul programului principal.\n");
return 0;
}
Programul principal apeleaz funcia n asamblare pe baza numelui. Parantezele
rotunde indic faptul c numele respectiv reprezint o funcie. Foarte important,
funcia trebuie declarat ca funcie extern. n acest caz, funcia nu necesit
parametri de intrare i nici nu returneaz vreo valoare. Urmtoarele comenzi creaz
executabilul i l ruleaz.
gcc -g stabs -o program main.c proc.o m32
./program
Mesaj din interiorul functiei in asamblare.
Mesaj din interiorul programului principal.
Codul compilat poate fi dezasamblat cu programul objdump:
objdump -d program -M intel -j .text

Vei observa c sunt prezente mai multe seciuni. Cele care ne intereseaz sunt
main i proc. Seciunea main conine codul n asamblare generat de compilator
pentru programul C.
080483f4 <main>:
80483f4:
55
80483f5:
89 e5
80483f7:
83 e4 f0
80483fa:
83 ec 10
80483fd:
e8 1e 00 00 00
8048402:
c7 04 24 00 85 04 08
8048409:
e8 16 ff ff ff
804840e:
b8 00 00 00 00
8048413:
c9
8048414:
c3

push ebp
mov ebp,esp
and esp,0xfffffff0
sub esp,0x10
call 8048420 <proc>
mov DWORD PTR [esp],0x8048500
call 8048324 <puts@plt>
mov eax,0x0
leave
ret

Seciunea proc conine codul funciei n asamblare:


08048420 <proc>:
8048420:
55
8048421:
89 e5
8048423:
83 ec 0c
8048426:
53
8048427:
56
8048428:
57
8048429:
68 18 a0 04 08
804842e:
e8 e1 fe ff ff
8048433:
83 c4 04
8048436:
5f
8048437:
5e
8048438:
5b
8048439:
89 ec
804843b:
5d
804843c:
c3

push
mov
sub
push
push
push
push
call
add
pop
pop
pop
mov
pop
ret

ebp
ebp,esp
esp,0xc
ebx
esi
edi
0x804a018
8048314 <printf@plt>
esp,0x4
edi
esi
ebx
esp,ebp
ebp

Funcia proc este apelat din programul principal n mod obinuit, cu


instruciunea CALL. n acelai mod n care funcia proc, la rndul su, apeleaz
funcia printf.

11.2. Linii de asamblare n codul surs C


11.2.1.

Sintaxa AT&T

nc din capitolul II am vzut c instruciunile main ale procesoarelor


x86 sunt codificate n limbaj de asamblare prin dou seturi de instruciuni
mnemonice. Totodat, ne amintim c instruciunea mnemonic reprezint numai o
modalitate prin care fiinele umane deduc ce nseamn pentru procesor o anumit
secven de cifre binare. n loc s scriem secvena de zero i unu 1000100
111000011, scriem MOV BX,AX. La fel de bine am putea scrie COPIAZ BX
n AX, dar, din nefericire, creatorul limbajului de asamblare nu a fost romn.
Folosim MOV BX,AX numai pentru c aa a sugerat Intel. Setul alternativ de
instruciuni mnemonice pentru procesoare x86 a aprut din dorina de a face
sistemul de operare Unix ct mai uor de implementat pe diferite arhitecturi
hardware. Aceste instruciuni se numesc mnemonici AT&T. Cum toate
instrumentele de dezvoltare software cu surs deschis (compilatorul GCC,
depanatorul GDB, asamblorul AS) sunt destinate unui sistem de operare nrudit cu
Unix, i anume Linux, toate folosesc nativ sintaxa AT&T. n procesul de
compilare, GCC nu face altceva dect s traduc codul surs C n cod surs scris n
limbajul de asamblare conform cu sintaxa AT&T. Ne amintim din capitolul III cum
compilatorul CC primete ca intrare un fiier surs .c i genereaz un fiier surs
.s, pe care l trimite apoi asamblorului GAS. Utilitarele GNU lucreaz n acelai
fel pe toate platformele. ntr-un sens, limbajul de asamblare este un limbaj
intermediar folosit numai n beneficiul compilatorului. De cele mai multe ori,
programatorii nu se vor ntlni cu el. Cu toate acestea, dac trebuie s lucrai cu
GCC i biblioteca standard C sau cu o mulime de alte biblioteci scrise n C i
pentru C, trebuie cel puin s v familiarizai cu mnemonicele AT&T. Mai ales
dac lucrai pe platforme Unix sau nrudite. S ne amintim cu arat un program n
aceast sintax:
gcc -S prog.c -o - -m32
.file
.text
.globl main
.type
main:
pushl
movl
subl

"prog.c"
main, @function
%ebp
%esp, %ebp
$16, %esp

push
mov
sub

ebp
ebp, esp
esp, 16

movl $40, -4(%ebp)


mov DWORD PTR [ebp-4], 40
movl $50, -8(%ebp)
mov DWORD PTR [ebp-8], 50
movl -4(%ebp), %eax
mov eax, DWORD PTR [ebp-4]
movl %eax, -12(%ebp)
mov DWORD PTR [ebp-12], eax
movl -8(%ebp), %eax
mov eax, DWORD PTR [ebp-8]
movl %eax, -4(%ebp)
mov DWORD PTR [ebp-4], eax
movl -12(%ebp), %eax
mov eax, DWORD PTR [ebp-12]
movl %eax, -8(%ebp)
mov DWORD PTR [ebp-8], eax
movl $0, %eax
mov eax, 0
leave
leave
ret
ret
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"
.section .note.GNU-stack,"",@progbits
n dreapta listingului apare echivalentul instruciunilor AT&T n sintaxa Intel.
Sintaxa Intel a fost obinut cu:
gcc -S prog.c -o - -masm=intel
Reguli generale impuse de sintaxa AT&T:
n sintaxa AT&T, comentariile sunt delimitate cu semnul # (diez). n
sintaxa Intel se folosete ; (punct i virgul).
Mnemonicele AT&T i numele registrelor sunt scrise ntotdeauna cu
caractere mici. Acest lucru respect faptul c Unix difereniaz literele
mari de cele mici. n timp ce Intel sugereaz utilizarea majusculelor, dar
accept literele mici, AT&T cere litere mici.
Numele de registre sunt ntotdeauna precedate de simbolul % (procent).
Acest lucru permite asamblorului s recunoasc numele de registre.
Intel: EAX
AT&T: %eax

n sintaxa AT&T, operandul destinaie este ntotdeauna n dreapta,


operandul surs este n stnga. Aadar, operanzii sunt n ordine invers
fa de sintaxa Intel.
ntotdeauna, la mnemonica instruciunilor main cu operanzi se adaug un
sufix de un caracter, sufix care indic dimensiunea operazilor. Caracterele
sufix sunt b (byte), w (word) i l (long), indicnd un octet, doi octei sau
patru octei. n caz contrar, GAS va ncerca s ghiceasc singur
dimensiunea operanzilor. Este indicat ca asamblorul s nu fie lsat s

ghiceasc, deoarece o poate face greit.


Intel: mov bx,ax
AT&T: movw %ax,%bx

n sintaxa AT&T, operanzii imediai i valorile constante sunt ntotdeauna


precedate de semnul $ (dolar). Acest lucru permite asamblorului s
recunoasc operanzi de tip imediat.
Intel: mov eax, _const
AT&T: movl $_const, %eax
Introducem imediatul 0A00BH n registrul EBX:
Intel: mov ebx, 0A00BH
AT&T: movl $0xa00b, %ebx
Introducem imediatul 255 n registrul AL:
Intel: mov al, 255
AT&T: movb $255, %al
ncarc n registrul ECX adresa variabilei globale C, total:
Intel: mov ecx, total
AT&T: movl $total, %ecx

n sintaxa AT&T, deplasamentul din metodele de adresare a memoriei sunt


valori cu semn plasate n afara parantezelor rotunde care conin baza,
indexul i factorul de scal.
Intel: [baz + index scal ) deplasament]
AT&T: deplasament (baz, index, scal)

Simbolul semnific faptul c deplasamentul este cu semn; poate fi


pozitiv sau negativ, lucru care indic dac aceast valoare se adun sau se scade
din restul adresei efective. n mod obinuit, vom ntlni explicit semnul minus
numai atunci cnd deplasamentul este negativ, lipsa semnului denot deplasament
pozitiv. Deplasamentul i factorul de scal sunt opionale.
Pentru adresarea indirect i direct, sintaxa AT&T folosete paranteze

rotunde, spre deosebire de sintaxa Intel, care folosete paranteze ptrate. De


exemplu,
mov eax, [ebx]
mov eax,[d]
este scris n AT&T ca
movl (%ebx), %eax
movl d,%eax
n acest ultim caz, observai diferena dintre:
Intel: mov eax,[d]
Intel: mov eax, d

AT&T: movl $d, %eax


AT&T: movl d, %eax

Prima instruciune ncarc n registrul EAX coninutul adresat de eticheta d, a doua


instruciune ncarc chiar adresa.
n sintaxa Intel, dac dorim s extragem un element de o anumit
dimensiune, folosim operatorii byte ptr, word ptr sau dword ptr, unde
ptr poate s lipseasc. De exemplu:
Intel: mov ax, word [ebp]
AT&T: movw (%ebp), %ax
sau
Intel: mov byte al, [ebx]
AT&T: movb (%ebx), %al
Dac exist deplasament, acesta trebuie poziionat n faa parantezelor:
Intel: mov dword eax, [ebx-4]
AT&T: movl -4(%ebx), %eax
Intel: mov al, byte [ebx + edi + 28]
AT&T: movb 28(%ebx,%edi), %al
Intel: mov ebx, [eax + _variabila]
AT&T: movl _variabila(%eax), %ebx
Cnd sunt folosite toate cmpurile modului de adresare a memoriei,
ntlnim expresii de genul:

Intel: mov edx, [ebx + edi * 8 + _vector]


AT&T: movl _vector(ebx,edi,8), %edx
Cteva expresii mai puin intuitive:
Intel: mov ebx, [eax * 4 + vector] ;adresare indexat
AT&T: movl vector(,%eax,4), %ebx
i
Intel: mov ecx, [eax + 1] ;adresare bazat
AT&T: movl 1(%eax), %ecx
Ne oprim aici. Ca not, amintii-v c putei folosi registrul ESP n interiorul
expresiei de calcul a adresei efective, dar numai ca registru de baz.

11.2.2.

Formatul de baz

Structura necesar unei funcii n asamblare pentru a putea fi introdus


direct n programele C (introducerea direct a instruciunilor de asamblare n
corpul C) nu difer foarte mult fa de structura funciilor n limbaj de asamblare
studiate pn acum. Singura diferen este c, de aceast dat, secvena de
instruciuni se afl chiar n fiierele surs C. Compilatorul GNU C este atenionat
c urmeaz o seciune scris n asamblare prin cuvntul cheie asm. Formatul de
baz al unei seciuni asm este urmtorul:
asm(cod n asamblare );
Codul n asamblare aflat ntre paranteze rotunde trebuie s respecte un format
specific:
Instruciunile trebuie introduse ntre ghilimele;
Dac sunt prezente mai multe instruciuni, acestea trebuie separate printrun caracter de linie nou, \n. Adesea, pentru a face liniile mai uor de citit,
este introdus i un caracter \t.
A doua regul este necesar deoarece compilatorul trateaz codul din seciunea
asm textual, plasndu-l n codul generat exact aa cum este scris. Urmtorul
exemplu reprezint funcia de ieire din program:
asm(movl $1, %eax\n\tmovl $0, %ebx\n\tint $0x80);
Deoarece formatul nu este foarte lizibil, majoritatea programatorilor scriu

instruciunile pe linii separate. n acest caz, fiecare instruciune trebuie nchis ntre
ghilimele.
asm ( movl $1, %eax\n\t
movl $0, %ebx\n\t
int $0x80);
Acest format este mai uor de citit i neles. Seciunea asm poate fi inclus
oriunde n codul surs. Urmtorul program demonstreaz cum arat o seciune asm
ntr-un program.
/* global.c AT&T Exemplu care utilizeaz variabile C */
#include <stdio.h>
int a = 50;
int b = 60;
int rez;
int main() {
asm ( pusha\n\t
movl a, %eax\n\t
movl b, %ebx\n\t
imull %ebx, %eax\n\t
movl %eax, rez\n\t
popa);
printf(the answer is %d\n, rez);
return 0;
}
Variabilele a, b i rez sunt definite n programul C ca variabile globale i sunt
folosite n seciunea asm. Observai c, n interiorul seciunii asm, valorile sunt
utilizate ca locaii de memorie i nu ca elemente de date imediate. Variabilele pot fi
de asemenea folosite n alt parte a programului.
Variabilele de date trebuie declarate ca globale. n interiorul seciunii asm nu
putem folosi variabile locale.
Codul n asamblare generat de compilator arat astfel:
gcc -S global.c -o - -m32
.file "global.c"

.globl a
.data
.align 4
.type a, @object
.size a, 4
a:
.long 50
.globl b
.align 4
.type b, @object
.size b, 4
b:
.long 60
.comm rez,4,4
.section
.rodata
.LC0:
.string
"the answer is %d\n"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $16, %esp
#APP
# 8 "global.c" 1
pusha
movl a, %eax
movl b, %ebx
imull %ebx, %eax
movl %eax, rez
popa
# 0 "" 2
#NO_APP
movl rez, %edx
movl $.LC0, %eax
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
movl $0, %eax
leave

ret
.size main, .-main
.ident"GCC: (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"
.section
.note.GNU-stack,"",@progbits
Observai c a i b au fost definite n segmentul datelor iniializate i au primit
valoare, iar rez a fost definit n segmentul datelor neiniializate. Sintaxa AT&T
definete dou directive prin care putem declara date neiniializate, .comm este
una dintre ele. Nu intrm n detalii.
Codul generat folosete prologul i epilogul C standard, ultimul
implementat prin instruciunea leave. n cadrul codului generat se afl o regiune
cuprins ntre simbolurile #APP i #NO_APP. Aceasta este seciunea care conine
codul specificat cu asm. Observai efectul caracterelor \n i \t asupra amplasrii
codului.
Din exemplu reiese nc o caracteristic important: secvena util de
instruciuni n asamblare trebuie precedat de instruciunea PUSHA i ncheiat cu
instruciunea POPA. Este important de reinut c nainte de execuia codului n
asamblare trebuie s salvai valorile iniiale ale registrelor, iar la final trebuie s
restaurai coninutul acestora deoarece este posibil ca GCC s foloseasc acele
registre n alt parte a codului surs (dac sunt modificate n interiorul seciunii
asm, pot aprea efecte nedorite).
Sintaxa Intel
Pn acum codul inserat n program a respectat sintaxa AT&T. A fost mai
uor pentru noi s folosim sintaxa AT&T pe o platform Unix, cu un compilator ce
recunoate nativ sintaxa AT&T. S vedem cum se modific programul anterior
dac specificm seciunea asm n sintaxa Intel.
/* global.c Intel - Exemplu care utilizeaz variabile C */
#include <stdio.h>
int a = 50;
int b = 60;
int rez;
int main()
{
asm (".intel_syntax noprefix\n\t"
"pusha\n\t"
"mov eax,a\n\t"
"mov ebx,b\n\t"
"imul eax,ebx\n\t"
"mov rez,eax\n\t"

"popa\n\t"
".att_syntax prefix");
printf("the answer is %d\n", rez);
return 0;
}
Observai c la nceputul secvenei n asamblare am inserat ".intel_syntax
noprefix\n\t". Aceasta indic asamblorului GAS c urmtoarea secven de
instruciuni n asamblare folosete sintaxa Intel. noprefix nseamn c registrele
nu au nevoie de simbolul % ca prefix. Ultima linie, ".att_syntax prefix",
indic asamblorului GAS c instruciunile urmtoare vor fi asamblate n sintaxa
implicit, AT&T. n acest caz, compilarea se face n mod obinuit:
gcc -g stabs -o global global.c m32
Dac lipsea ultima linie din seciunea asm, ".att_syntax noprefix",
puteam obine executabilul fornd compilatorul GCC s tranduc tot codul n
sintaxa Intel:
gcc -g stabs -o global global.c -masm=intel m32
Modificatorul volatile
Cnd introducem instruciuni n asamblare n programe C trebuie s ne
gndim ce efect ar putea avea procesul de compilare asupra acestora. n etapele de
transformare a codului surs n cod de asamblare, compilatorul poate ncerca s
optimizeze codul scris n asamblare n vederea creterii performanei. De obicei,
acest lucru are loc prin eliminarea funciilor neutilizate, partajarea registrelor ntre
variabile care nu sunt folosite n acelai timp sau rearanjarea codului pentru o
parcurgere mai uoar.
ns este posibil ca optimizarea s produc efecte nedorite, n special
asupra funciilor din seciunea asm. Modificatorul volatile, plasat imediat
dup cuvntul cheie asm, indic compilatorului faptul c nu este dorit optimizarea
acelei seciuni de cod. Formatul este urmtorul:
asm volatile (cod n asamblare);
Utilizarea __asm__
n unele cazuri, cuvntul asm, utilizat la identificarea unei seciuni de cod
n asamblare, trebuie modificat. Specificaiile ANSI C utilizeaz cuvntul asm n
alte scopuri, lucru care mpiedic utilizarea sa ca identificator de seciune n
asamblare. Dac scriei cod utiliznd conveniile ANSI C, trebuie s utilizai

cuvntul cheie __asm__ (dou caractere underscore). Codul n asamblare din


interiorul seciunii nu se modific. Dac avem nevoie de modificatorul
volatile, acesta trebuie scris __volatile__.
__asm__ __volatile__ (".intel_syntax noprefix\n\t"
"pusha\n\t"
"mov eax,a\n\t"
"mov ebx,b\n\t"
"imul eax,ebx\n\t"
"mov rez,eax\n\t"
"popa\n\t"
".att_syntax prefix");

11.2.3.

Formatul extins

Formatul de baz are cteva limitri. n primul rnd toate valorile de intrare
i de ieire trebuie s utilizeze variabile globale definite n program. n plus, trebuie
s fim extrem de ateni s nu modificm valorile vreunui registru. Formatul extins
pune la dispoziie cteva opiuni adiionale ce ne permit s controlm exact modul
n care este interpretat codul n asamblare. Formatul versiunii extinse a seciunii
asm arat astfel:
asm (cod n asamblare : locaii de ieire : operanzi
de intrare : lista de modificri);
Acest format are patru seciuni, fiecare separat prin dou puncte:
cod n asamblare - propoziiile n asamblare propriu zise;
locaii de ieire - registre i/sau locaii de memorie ce vor conine valorile
de ieire la finalul codului n asamblare;
operanzi de intrare - registre i/sau locaii de memorie ce conin valorile de
intrare necesare codului din seciunea scris n asamblare;
lista de modificri - registre ce vor fi modificate de ctre codul n
asamblare.
Prezena tuturor seciunilor nu este obligatorie. Dac codul n asamblare nu
genereaz valori de ieire, seciunea locaiilor de ieire poate lipsi. n schimb,
separarea seciunilor prin dou puncte se pstreaz. Doar dac seciunea codului n
asamblare nu modific niciun registru ultimele dou puncte pot fi omise.
n formatul asm de baz valorile de intrare i ieire erau definite ca
variabile globale n programul C i ncorporate n seciunea n asamblare pe baza

numelui acestora. n formatul extins putem atribui valori de intrare i ieire att
registrelor ct i locaiilor de memorie. Formatul listei de valori de intrare i ieire
este:
caracter_de_control (variabil)
unde variabila este o variabil C declarat n program. n formatul asm extins pot
fi folosite att variabile globale ct i locale. Caracterul de control (constraint)
specific unde trebuie plasat variabila (pentru valori de intrare) sau unde trebuie
depozitat (pentru valori de ieire). Acesta definete locaia final a unei variabile:
registru sau locaie de memorie.
Tabelul 11.2 Caractere de control

Caracter de control
a
b
c
d
S
D
r
q
A
f
m
V
i
n
g

Descriere
Folosete registrele %eax, %ax sau %al
Folosete registrele %ebx, %bx sau %bl
Folosete registrele %ecx, %cx sau %cl
Folosete registrele %edx, %dx sau %dl
Folosete registrele %esi sau %si
Folosete registrele %edi sau %di
Folosete orice registru de uz general
Folosete registrul %eax, %ebx, %ecx sau %edx
Combin registrele %eax i %edx pentru valori de 64 de bii
Folosete un registrul pentru variabile n virgul mobil
Folosete locaia de memorie a variabilei
Folosete numai o locaie de memorie direct
Folosete un ntreg imediat
Folosete un ntreg imediat de valoare cunoscut
Folosete orice registru sau locaie de memorie disponibil

Pe lng caracterele de control, valorile de ieire pot include modificatori de


control. Modificatorii de control indic modul n care compilatorul trebuie s
trateze operanzii.
Tabelul 11.3 Modificatori de control

Modificator de ieire Descriere


+
Operandul poate fi att citit ct i scris (read-write)
=
Operandul poate fi numai scris (write-only). Valoarea
anterioar este nlocuit cu valoarea de ieire curent.
%
Operandul poate fi schimbat cu urmtorul dac este necesar
&
Operandul poate fi ters i reutilizat nainte de finalizarea
codului n asamblare.

Urmtorul exemplu
asm volatile (".intel_syntax noprefix\n\t"
"cld\n\t"
"rep\n\t"
"stosl\n\t"
".att_syntax prefix\n\t"
: /* nicio locaie de ieire */
: "c" (contor), "a" (valoare), "D" (dest)
: "%ecx", "%edi"
);
ncarc valoarea aflat n registrul EAX n locaia de memorie indicat de
adresa dest, de un numr de ori contor. Linia operanzilor de intrare
: "c" (contor), "a" (valoare), "D" (dest)
ncarc contor n ECX, valoare n EAX i dest n EDI. Aceste informaii
pot ajuta compilatorul s optimizeze codul. De exemplu, n timpul operaiunilor de
alocare a registrelor compilatorul ar putea aranja ca valoarea s se afle deja n
registrul EAX sau, dac codul n asamblare s-ar afla ntr-o bucl, ar putea pstra
coninutul EAX de-a lungul execuiei acesteia.
Observai c nu am specificat nimic n seciunea locaiilor de ieire. Nu
este necesar ca valorile de ieire s fie ntotdeauna precizate. Valorile de intrare ale
unor instruciuni indic i valorile de ieire. n cazul nostru, valoarea de ieire este
deja definit ca fiind una din valorile de intrare (adresa indicat de registrul EDI),
aadar aceasta nu mai trebuie specificat n cmpul locaiilor de ieire. Dar,
deoarece nu am definit explicit nicio valoare de ieire, este important s folosim
cuvntul cheie volatile. n caz contrar, din moment ce seciunea asm nu
produce niciun rezultat, compilatorul poate considera toat seciunea inutil i
renun la ea (nu o introduce n fiierul executabil).
Lista de modificri
: "%ecx", "%edi"
indic compilatorului faptul c registrele EAX i EDI au fost rescrise
(valorile acestora la finalul secvenei n asamblare difer de cele avute la intrarea n
seciune). n lista de modificri registrele sunt scrise cu prefixul %. Dac scriem
ntr-o locaie de memorie trebuie s includem n list cuvntul memory. Dac
sunt modificai indicatorii de condiii din registrul EFLAGS este indicat s
introducem n lista de modificri operatorul cc (conditional codes), care anun

compilatorul de aceste modificri.


Din nefericire, directivele extinse folosesc sintaxa AT&T, din acest motiv
am inserat n faa acestora opiunea ".att_syntax prefix". Sintaxa GNU
Intel definete numai sintaxa pentru codurile de instruciune, nu pentru directive,
funcii, macroinstruciuni, etc.. Directivele folosesc nc sintaxa AT&T. Se poate
ntampla s fie nevoie s comutm de la o sintax la alta chiar i n interiorul
secvenei n asamblare. n special atunci cnd lucrm cu operanzi aflai n
memorie, deoarece mecanismul de substituie al operanzilor folosete sintaxa
AT&T. Din acest motiv, n exemplele urmtoare vom folosi numai sintaxa AT&T.
Definirea registrelor
Urmtoarea secven demonstreaz cum putem declara registre n formatul
asm extins.
asm

(imull %%edx, %%ecx\n\t


movl %%ecx, %%eax
: =a(rez)
: d(val1), c(val2));

Registrul de ieire este modificat cu semnul egal; indicm faptul c asupra lui pot fi
efectuate numai operaii de scriere. Compilatorul ncarc valorile variabilelor
val1 i val2 n registrele EDX i ECX. Val1 i val2 pot fi variabile globale
sau locale. n ultimul caz, ele se gsesc n zona de stiv a programului. Rezultatul
generat n registrul EAX este apoi transferat n variabila rez. Observai c
registrele au ca prefix dou semne %% n loc de unul singur. Acest lucru este
necesar deoarece, n secvena n asamblare, fiecare operand este adresat de
compilator pe baza unui numr (placeholder) precedat de semnul %. Compilatorul
atribuie fiecrei valori de intrare sau ieire prezente n cmpurile formatului extins
un numr, pe baza poziiei sale n list, ncepnd cu zero. De exemplu:
asm (cod n asamblare
: =r (rez)
: r (val1), r (val2)
);
va produce urmtoarele coduri:
%0, pentru registrul care conine variabila rez.
%1, pentru registrul care conine variabila val1.
%2, pentru registrul care conine variabila val2.
Metoda permite utilizarea n secvena de asamblare att a registrelor ct i a

locaiilor de memorie. Numrul total de operanzi este limitat la zece sau la numrul
maxim de operanzi pe care l poate lua o instruciune (care este mai mare). Codul
n asamblare arat astfel:
imull %1, %2
movl %2, %0
Deoarece GCC identific operanzii pe baza %0, %1, .a.m.d, %edx ar fi interpretat
ca fiind parametrul %e, care nu exist i, n consecin, ar fi ignorat. Apoi ar
ncerca s gseasc simbolul dx, simbol invalid, deoarece nu are prefixul %, dar
care oricum nu era ce se intenionase.
Aadar, pentru o secven n asamblare, de exemplu una care nmulete o
valoare cu 5, putem declara registrele explicit
asm ("leal (%%ebx,%%ebx,4), %%ebx"
: "=b" (x)
: "b" (x) );
caz n care trebuie s avem grij s folosim dou simboluri procent, %%, sau putem
permite compilatorului s aleag registrele. Cu excepia cazului n care avem
nevoie explicit de un anumit registru, cel mai bine este s permitem compilatorului
s aleag. Numrul registrelor este limitat i se poate ntampla ca GCC s nu poat
folosi registrele specificate fr s ascund valorile anterioare.
asm ("leal (%1,%1,4), %0"
: "=r" (x)
: "r" (x) );
Mai mult, dac dorim ca variabila s foloseasc acelai registru, att pentru intrare
ct i pentru ieire, putem specifica registrul atribuit pe baza codului %0.
asm ("leal (%0,%0,4), %0"
: "=r" (x)
: "0" (x) );
Dac se lucreaz cu un numr mare de valori de intrare i ieire, metoda numeric
poate deveni problematic. De aceea, compilatorul GNU pune la dispoziie o
variant alternativ, i anume, declararea unor denumiri. Numele este declarat n
seciunea care definete valorile de intrare sau ieire i respect urmtorul format:
%[nume] caracter_de_control (variabil)

n acest caz, valoarea nume devine identificatorul variabilei.


asm (imull %[valoare1], %[valoare2]
: [valoare2] =r(val2)
: [valoare1] r(val1), 0(val2));

Bibliografie

1. Intel Corporation, Software Developers Manual Basic


Architecture, vol. 1, Order Number 243190, 1997.
2. Intel Corporation, Software Developers Manual Instruction Set
Reference, vol. 2, Order Number 243191, 1997.
3. Intel Corporation, Software Developers Manual System
Programming Guide, vol. 3, Order Number 243192, 1997.
4. Intel Corporation, Intel 80386 Programmers Reference Manual,
1986.
5. Iulian Bdescu, Microprocesoare, Printech, Bucureti, 2002,
ISBN: 973-652-547-3
6. Ubuntu documentation team, Ubuntu Server Guide, 2012,
https://help.ubuntu.com/12.04/serverguide/serverguide.pdf.
7. Vasile Lungu, Procesoare INTEL, Programarea n limbaj de
asamblare, Ediia a II-a, Editura Teora, 2004, ISBN: 973-200099-6.
8. Jeff Dunteman, Assembly Language Step-by-Step, Wiley
Publishing, Inc., 2009, ISBN: 978-0-470-49702-9.
9. Sivarama Dandamudi, Guide to Assembly Language
Programming in Linux, Springer Science+Bussiness Media, Inc.,
2005, ISBN-10: 0-387-25897-3.
10. Richard Stallman, Roland Pesch, Stan Shebs, et al., Debugging
with GDB The GNU Source-Level Debugger, Ninth Edition,
ver. 20040217, Free Software Foundation, ISBN: 1-882114-77-9.
11. Peter Jay Salzman, Using GNUs GDB Debugger, Tutorial,
http://www.cs.cmu.edu/~gilpin/tutorial/
12. Richard Blum, Professional Assembly Language,
Wiley
Publishing, Inc., 2005, ISBN: 0-7645-7901-0.
13. Bob Nevelin, Linux Assembly Language Programming, First
Edition, Prentice Hall, Inc., 2000, ISBN: 0-13-087940-1.
14. Paul Carter, PC Assembly Language, 2006, http://www.dr
paulcarter.com/pcasm/
15. Robert Platz, Introduction to Computer Organization with x86-64
Assembly Language & GNU/Linux, 2011, http://bob.cs.sonoma.
edu/IntroCompOrg_Jan_2011.pdf
16. Yariv Kaplan, Introduction to Protected-Mode, 1997, Articol,

17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.

Internals.com, http://www.internals.com/articles/protmode/introd
uction.htm
Randal Bryant, David OHallaron, Computer Systems A
Programmers Perspective, 2nd Edition, Pearson Education, Inc,
2011, ISBN: 0-13-610804-0.
Drago Acostchioaie, Programare C i C++ pentru Linux,
Editura Polirom, 2002, ISBN: 973-681-112-3.
blog.interlinked.org, Vim Introduction and Tutorial, Tutorial,
http://blog.interlinked.org/tutorials/vim_tutorial.html
Free Software Foundation, GCC 4.6.3 Manual, 2010, http://gcc.
gnu.org/onlinedocs/gcc-4.6.3/gcc/
NASM Development Team, NASM The Netwide Assembler,
ver. 2.10.01, 2012, http://www.nasm.us/xdoc/2.10.01/nasmdoc.pdf
Peter Johnson, Yasm User Manual, 2012, http://www.tortall.net
/projects/yasm/manual/manual.pdf
Tool Interface Standards, Executable and Linkable Format
(ELF), Portable Formats Specification, ver. 1.1, http://www.sky
free.org/linux/references/ELF_Format.pdf
Phillip, Using Assembly Language in Linux, Articol, 2001,
http://asm.sourceforge.net//articles/linasm.html
Bharata Rao, Inline assembly for x86 in Linux, Articol, 2001,
http://www.ibm.com/developerworks/linux/library/l-ia/index.html
Brennan Underwood, Brennans Guide to Inline Assembly, ver.
1.1.2.2, Articol, http://www.delorie.com/djgpp/doc/brennan/bren
nan_att_inline_djgpp.html
Ram Narayan, Linux assemblers: A comparison of GAS and
NASM, Articol, 2007, http://www.ibm.com/developerworks/linux
/library/l-gas-nasm/index.html
iecc.com, Dynamic Linking and Loading, rev. 2.3, Articol, 1999,
http://www.iecc.com/linker/linker10.html
Ashish Bansal, Shared objects for the object disoriented!,
Articol, 2001, http://www.ibm.com/developerworks/library/lshobj/
Baris Simsek, Libraries, EnderUNIX Software Development
Team,
2004,
http://www.enderunix.org/simsek/articles/
libraries.pdf

S-ar putea să vă placă și