Documente Academic
Documente Profesional
Documente Cultură
George Mois
November 12, 2021
Abstract
Procesul ”build”, analiza codului, numere reale s, i preluarea de date
de la utilizator.
1 Introducere
Un compilator este un program pe calculator care transformă codul sursă (scris
ı̂ntr-un anumit limbaj sursă) ı̂ntr-un alt limbaj numit limbaj t, intă. De obicei,
dar nu ı̂ntotdeauna, limbajul sursă este un limbaj de programare pe care ı̂l
ı̂nt, eleg oamenii (programatorii), cum sunt Java, C, Scala, sau altele. De obicei,
dar nu ı̂ntotdeauna, limbajul t, intă constă ı̂n cod obiect care poate fi executat de
către o platformă hardware (ex. x86-32, x86-64, ARM7, powerpc, etc.). Cu alte
cuvinte, un compilator traduce dintr-un limbaj ı̂ntr-un altul. Un compilator nu
este un obiect magic, ci este un program ca s, i oricare altul s, i este scris ı̂ntr-un
anumit limbaj de programare (limbajul de implementare) de către programatori.
O problemă la care s-ar putea gândi cineva este modul ı̂n care am putea scrie
un compilator fără a avea la ı̂ndemâna un compilator. Acest lucru este posibil,
dar ı̂n ziua de astăzi avem o gamă largă de compilatoare pe care le putem folosi.
Compilatoarele au un set comun de componente:
• front-end
– partea care se ocupă de corectitudinea programului ı̂n ceea ce prives, te
semantica
– are ca rezultat o reprezentare intermediară a programului de intrare
– lexical checking, syntax checking, type checking, etc.
• middle-end
– optimizarea programului prin rescrierea reprezentării intermediare
– ex. dead code removal, constant propagation, etc.
• back-end
– translatarea reprezentării intermediare la limbajul t, intă adăugând
optimizări specifice platformei folosite
– ex. limbaj de asamblare pentru platforma t, intă
1
Înainte s, i după compilare mai sunt necesari o serie de pas, i pentru a ajunge
de la sursă la t, intă. Înainte de compilare s-ar putea să fie nevoie să rulăm codul
ı̂ntr-un preprocesor care pregătes, te codul pentru compilare (ı̂n C, preprocesorul
rezolvă toate directivele preprocesor – #directivă). După compilare este nevoie
să combinăm diferite obiecte compilate cu bibliotecile necesare ı̂ntr-un program
compilat, iar mai apoi să asamblăm (assemble) codul de asamblare ı̂n as, a fel
ı̂ncât el să poată fi rulat de către platforma hardware sau de către sistemul de
operare. Aces, ti din urmă pas, i sunt executat, i de un linker sau un link editor.
As, adar, procesul de build include pas, ii enumerat, i mai sus, s, i anume: pre-
procesare, compilare s, i link editare. Astfel, compilarea este ceea ce se ı̂ntâmplă
ı̂ntre preprocesare s, i link editare, doar că, datorită faptului că ı̂n general se
foloses, te un singur tool pentru a lansa ı̂ntregul proces de build, termenul com-
pilare este de foarte multe ori folosit pentru a reprezenta procesul de build.
• compilation stage,
• linking stage.
2
• compilare condit, ională – #if,#ifdef,#ifndef s, i altele.
Cel mai des se foloses, te prima dintre acestea, o directivă include referind un
fis, ier extern prin nume. Directiva este ı̂nlocuită de cont, inutul fis, ierului referit ca
s, i cum acesta ar fi fost scris la locat, ia ei. Principalul beneficiu este factorizarea,
care permite reutilizarea cont, inutului unui fis, ier, fiind necesară ment, inerea unei
singure copii a acestuia. Despre ultimele două directive vom vorbi ı̂ntr-unul din
laboratoarele următoare.
Fis, ierul produs de către preprocesor are adnotări intercalate de forma
# nr rand ”fisier” ..., care specifica provenient, a fiecărei linii de cod. Acest
lucru este necesar pentru raportarea erorilor s, i a atent, ionărilor ı̂n cazul ı̂n care
acestea apar după ce compilatorul verifică semantica limbajului din cod. Pentru
a avea un sens pentru programator, locat, iile erorilor s, i a atent, ionărilor trebuie
să indice liniile din fis, ierele pe care acesta le editează.
În cazul STM32CubeIDE, atunci când accesăm comanda Build, codul trece
prin următoarele transformări:
Figure 1: Dissasembly
• preprocesare
– generarea unui fis, ier cu cont, inutul obt, inut după rularea C:\ >gcc -E main.c
• parsare
– verificarea sintaxei instruct, iunilor din fis, ierul generat la preprocesare
(extensia i );
– componenta ”code generator” va transforma fis, ierul .i ı̂ntr-un fis, ier
.s care cont, ine instruct, iuni ı̂n limbaj de asamblare;
– fiecare instruct, iune C va fi transformată ı̂n mnemonicile corespunzătoare
(Fig. 1);
• producerea de fis, iere obiect
– assemblerul va converti menmonicile ı̂n coduri mas, ină – rezultă fis, iere
obiect, cu extensia .o;
3
– fis, ierul obiect este o colect, ie de coduri mas, ină corespunzătoare codu-
lui C scris init, ial;
• link editarea fis, ierelor obiect
– combinarea fis, ierelor obiect ı̂ntr-un fis, ier executabil – producerea ex-
ecutabilului final;
– ı̂n cazul nostru fis, ierul generat este un fisier .elf
∗ executable and link format;
∗ este un fis, ier executabil s, i un fis, ier de depanare (debug);
• postprocesarea executabilului final – opt, ional
– objcopy tool – generarea unui fis, ier .bin – pure binary executable file.
1.2.1 Exemplu
#include<s t d i o . h>
// G l o b a l v a r s
int g d a t a 1 = −4000;
int g d a t a 2 = 2 0 0 ;
int g r e s u l t = 0 ;
p r i n t f ( ” R e s u l t i s %d\n” , g r e s u l t ) ;
for ( ; ; ) ;
}
4
Figure 2: Rezultat build
În directorul proiectului (subdirectorul Debug) pot fi văzute toate fis, ierele
generate. Fisierele .i si .s nu sunt generate pentru utilizator. Pentru salvarea
lor, trebuie adăugat flag-ul din Figura 3.
Fis, ierele .s cont, in mnemonici (instruct, iuni generate pentru fiecare instruct, iune
C) specifice arhitecturii procesorului (Instruction Set Document of ARM Cortex-
M). Fisierele .o sunt generate ı̂n subdirectorul Src, dar cont, in cod mas, ină care
nu poate fi vizualizat cu editoarele text.
5
2 Analiza codului embedded
2.1 Microcontrolere
Un microcontroler este un computer plasat ı̂ntr-un singur cip. Acest lucru face
ca memoria, viteza, interfet, ele externe, s, i altele, să fie mult sub performantele
unui calculator personal. Oricum, aceasta nu este o problemă de vreme ce
microcontrolerele sunt folosite ı̂n aplicat, ii embedded, unde doar o parte din
funct, ionalităt, i sunt de interes, ı̂n funct, ie de cerint, e.
Un microcontroler tipic (Figura 4) este format dintr-un procesor, memorii
volatile s, i nevolatile, pini de intrare s, i de ies, ire, periferice, ceasuri, magistrale,
toate ı̂n aceeas, i capsulă (ı̂n acelas, i cip).
6
2.2 Memoria pentru cod
Programul este format din instruct, iuni s, i din datele constante ale programului.
Există diferite tipuri de memorie pentru stocarea codului.
2.3 Exemplu
Continutul consolei la lansarea comenzii Debug ı̂n cazul STM32CubeIDE.
S T M i c r o e l e c t r o n i c s ST−LINK GDB s e r v e r . V e r s i o n 5 . 5 . 0
Copyright ( c ) 2 0 1 9 , S T M i c r o e l e c t r o n i c s . A l l r i g h t s r e s e r v e d .
S t a r t i n g s e r v e r with t h e f o l l o w i n g options :
P e r s i s t e n t Mode : Disabled
Logging L e v e l : 1
L i s t e n Port Number : 61234
S t a t u s R e f r e s h Delay : 15 s
Verbose Mode : Disabled
SWD Debug : Enabled
InitWhile : Enabled
Waiting f o r debugger c o n n e c t i o n . . .
Debugger c o n n e c t e d
7
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
STM32CubeProgrammer v2 . 4 . 0
−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−
Memory Programming . . .
Opening and p a r s i n g f i l e : ST−LINK GDB server a06772 . s r e c
File : ST−LINK GDB server a06772 . s r e c
Size : 5004 Bytes
Address : 0 x08000000
E r a s i n g memory c o r r e s p o n d i n g t o segment 0 :
E r a s i n g i n t e r n a l memory s e c t o r 0
Download i n P r g o r e s s :
F i l e download c o m p l e t e
Time e l a p s e d d u r i n g download o p e r a t i o n : 0 0 : 0 0 : 0 0 . 8 2 8
Verifying . . .
Download v e r i f i e d s u c c e s s f u l l y
8
2.4 Folosirea Memory Browser
În Memory Browser (Window - Show View - Memory Browser ) va trebui să
tastăm adresa de bază a memoriei program a microcontrolerului folosit (ı̂n man-
ualul de referint, ă a microcontrolerului folosit - sect, iunea referitoare la memoria
Flash).
2.5 Exercit, iu
Care este adresa de bază a memoriei principale ı̂n Flash pentru microcontrolerul
folosit? Vizualizat, i memoria ı̂ncepând de la această locat, ie folosind Memory
Browser. Afis, area valorilor se face cuvânt cu cuvânt, adică ı̂n grupuri de cate
32 de bit, i.
Pentru afis, are octet cu octet folosit, i meniul contextual din Memory Browser :
Columns - 1, Cell Size 1 byte.
2.7 Exercit, iu
Găsit, i adresa memoriei SRAM din manualul de referint, ă s, i vizualizat, i memoria
ı̂ncepând de la această adresă. Schimbat, i modul de afis, are al memoriei la Cell
Size 4 bytes s, i Radix la Signed Integer. Ce valoare avet, i la prima locat, ie?
9
.rodata este sect, iunea care cont, ine datele ”read only” ale programului.
.data este sect, iunea care cont, ine datele programului. Aici avem o coloana
care se numes, te LMA (Load Memory Address) s, i una care se numes, te VMA
(Virtual Memory Address). LMA este sursa din Flash, iar VMA este destinat, ia
datelor ı̂n SRAM. Valorile sunt copiate din LMA ı̂n VMA de către codul din
fis, ierul Startup care rulează pe microcontroler, mai exact de către rutina Re-
set Handler (cod de asamblare). Reset Handler este prima rutină executată de
către hardware la ı̂nceputul operării microcontrolerului.
3.1 Exercit, iu
Vizualizat, i cont, inutul memoriei de la adresa LMA. Găsit, i Reset Handler ı̂n
fisierul Startup. Observat, i că la final, această funct, ie apelează funct, ia main.
4 Dezasamblare – Disassembly
Aceasta este funct, ionalitatea IDE-ului care permite traducerea din cod mas, ină
ı̂ntr-un limbaj de nivel mai ı̂nalt. Acest proces este necesar pentru depanarea
la nivel de instruct, iune (care sunt instruct, iunile generate pentru codul scris de
către noi, optimizare).
Selectati Window - Show View - Dissasembly. În fereastra Dissasembly vom
vedea instruct, iunile ARM corespunzătoare codului scris de către noi.
10
O discut, ie mai amănunt, ită despre asamblare este ı̂n afara scopului acestui
laborator.
5.1 Exemplu
Numere foarte mici.
Încărcarea unui electron, care are valoarea de -1.60217662 x 10-19 .
Numere foarte mari.
Distante cosmice, ca s, i distanta de la Pământ la Galaxia Andromeda, cea
mai apropiată galaxie de Calea Lactee.
2.5 milioane ani lumina, sau 2.3651826 x 1019 Km
Aceste valori nu pot fi stocate folosind reprezentarea lor binară, ci doar
informat, iile despre ele:
• semn – 1 bit
• mantisa – 8 bit, i
• exponent (19) – 23 bit, i
• semn – 1 bit
• mantisa – 11 bit, i
• exponent (19) – 52 bit, i
11
5.4 Numere reale ı̂n C
1. float
• precizie simplă, 32 bit, i
• precizie de până la 6 pozit, ii zecimale
• 1.2 x 10-38 ... 3.4 x 1038
2. double
5.5 Exemplu
Rulat, i codul de mai jos:
#include<s t d i o . h>
// p r i n t t h e number i n d i f f e r e n t f o r m a t s
// p r i n t 6 d e c i m a l p l a c e s , a p p r o x i m a t e
p r i n t f ( ”No . i s %f ( 6 d e c i m a l p l a c e s ) . \ n” , no ) ;
// p r i n t up t o 9 d e c i m a l p l a c e s .
p r i n t f ( ”No . i s %0.9 f ( 9 d e c i m a l p l a c e s ) . \ n” , no ) ;
// Loss o f p r e c i s i o n
// p r i n t i n s c i e n t i f i c n o t a t i o n , a p p r o x i m a t e
p r i n t f ( ”No . i s %e ( 6 d e c i m a l p l a c e s ) . \ n” , no ) ;
// p r i n t i n s c i e n t i f i c n o t a t i o n , approximate , up t o 2 d e c i m a l p o i n t s
p r i n t f ( ”No . i s %0.2 e ( 2 d e c i m a l p l a c e s ) . \ n” , no ) ;
12
// change d a t a t y p e t o d o u b l e
double n o d o u b l e = 3 2 . 4 3 5 6 7 8 9 8 3 4 9 ;
// p r i n t t h e number i n d i f f e r e n t f o r m a t s
// p r i n t up t o 14 d e c i m a l p l a c e s , a p p r o x i m a t e
p r i n t f ( ”No i s %0.14 l f ( double , 14 d e c i m a l p l a c e s ) . \ n” , n o d o u b l e ) ;
//No l o s s o f p r e c i s i o n
// c h a r g e o f an e l e c t r o n , f l o a t number
f l o a t c = −1.60217662 e −19;
// p r i n t 6 d e c i m a l p l a c e s , a p p r o x i m a t e
p r i n t f ( ” Charge o f an e l e c t r o n i s %f ( 6 d e c i m a l p l a c e s ) . \ n” , c ) ;
// p r i n t i n s c i e n t i f i c n o t a t i o n , 6 d e c i m a l p l a c e s
p r i n t f ( ” Charge o f an e l e c t r o n i s %e ( 6 d e c i m a l p l a c e s ) . \ n” , c ) ;
// p r i n t 8 d e c i m a l p l a c e s , a p p r o x i m a t e
p r i n t f ( ” Charge o f an e l e c t r o n i s %0.8 f ( 8 d e c i m a l p l a c e s ) . \ n” , c ) ;
// p r i n t i n s c i e n t i f i c n o t a t i o n , 8 d e c i m a l p l a c e s
p r i n t f ( ” Charge o f an e l e c t r o n i s %0.8 e ( 8 d e c i m a l p l a c e s ) . \ n” , c ) ;
// Loss o f p r e c i s i o n
// c h a r g e o f e l e c t r o n , change d a t a t y p e t o d o u b l e , s i n c e poor a p p r o x i m a t i o
double c d o u b l e = −1.60217662 e −19;
// p r i n t 6 d e c i m a l p l a c e s , a p p r o x i m a t e
p r i n t f ( ” Charge o f E i s %0.28 l f ( dbl , 28 dec p l a c e s ) . \ n” , c d o u b l e ) ;
// p r i n t i n s c i e n t i f i c n o t a t i o n , 8 d e c i m a l p l a c e s
p r i n t f ( ” Charge o f E i s %0.8 l e ( dbl , 8 dec p l a c e s ) . \ n” , c d o u b l e ) ;
return 0 ;
}
13
Table 2: Litere de specificatori de format s, i tipurile lor de date corespunzătoare
Letter Data type of input data
c Scans a character (char).
s Scans a character string (char *).
d Scans an integer as a signed decimal number (int).
u Scans an integer as an unsigned decimal number (unsigned int).
o Scans an integer as an octal number.
x, X Scans an integer as a hexadecimal number.
hd, hu Scans an integer as a short decimal number (short int),
unsigned short decimal number (unsigned short int).
ld, lo, lx, lX Scans an integer as long decimal, long octal, long hexadecimal.
f Scans a floating-point number (float).
lf Scans as a double floating-point number (double).
Lf Scans as a long double floating-point number (long double).
– * (opt, ional) – caracter care indică faptul că rezultatul conversiei datelor
de intrare nu va fi atribuit niciunei variabile;
– lăt, ime (opt, ional) – număr zecimal care defines, te lungimea maximă a câmpului
controlat de specificatorul de format;
– tip – una sau două litere care definesc tipul de date al rezultatului con-
versiei.
Literele care definesc tipul datelor de conversie sunt date ı̂n Tabela 2.
Caracterul & ı̂n fat, a numelui fiecărei variabile indică sistemului unde să
stocheze valoarea introdusă. Astfel se oferă adresa variabilei. Nu avem carac-
terul & ı̂n fat, a numelui variabilei dacă variabila este o matrice unidimensională
(ex. sir de caractere).
6.2 Exemplu
p r i n t f ( ” Enter a c h a r a c t e r : ” ) ;
s c a n f ( ”%c ” , &t e s t c h a r ) ;
p r i n t f ( ” C h a r a c t e r e n t e r e d = %c \n” , t e s t c h a r ) ;
p r i n t f ( ” Enter a c h a r a c t e r s t r i n g : ” ) ;
s c a n f ( ”%s ” , t e s t s t r i n g ) ;
14
p r i n t f ( ” C h a r a c t e r s t r i n g e n t e r e d = %s \n” , t e s t s t r i n g ) ;
p r i n t f ( ” Enter an i n t e g e r : ” ) ;
s c a n f ( ”%d” , &t e s t i n t e g e r ) ;
p r i n t f ( ” I n t e g e r v a l u e e n t e r e d = %d\n” , t e s t i n t e g e r ) ;
p r i n t f ( ” O c t a l v a l = %#o , Hex v a l = %#X\n” , t e s t i n t e g e r , t e s t i n t e g e r ) ;
p r i n t f ( ” Enter a f l o a t number : ” ) ;
s c a n f ( ”%f ” , &t e s t f l o a t ) ;
p r i n t f ( ” F l o a t v a l u e e n t e r e d = %f \n” , t e s t f l o a t ) ;
return 0 ;
}
Observat, ie! Dacă folosit, i consola STM32CubeIDE, atunci trebuie sa folosit, i
apeluri de funct, ii fflush(stdout) după fiecare apel al funct, iei printf.
6.3 Citirea de valori multiple ı̂n cadrul aceleias, i funct, ii
scanf
int main ( )
{
int a , b , c ;
p r i n t f ( ” Enter t h r e e i n t e g e r numbers : ” ) ;
f f l u s h ( stdout ) ;
s c a n f ( ”%d %d %d” , &a , &b , &c ) ;
p r i n t f ( ”The numbers a r e %d , %d , and %d . \ n” , a , b , c ) ;
f f l u s h ( stdout ) ;
return 0 ;
}
15
getchar returnează caracterul citit ca o valoare unsigned char convertită
ı̂ntr-o valoare int la execut, ia funct, iei cu succes.
Apelul funct, iei getchar blochează programul până când se apasă o tastă
urmată de apăsarea tastei ENTER. Poate fi folosită pentru blocarea programului
s, i ment, inerea terminalului deschis atunci când se ajunge la finalul funct, iei main.
6.6 Exemplu
int main ( )
{
int a , b , c ;
p r i n t f ( ” Enter t h r e e i n t e g e r numbers : ” ) ;
f f l u s h ( stdout ) ;
s c a n f ( ”%d %d %d” , &a , &b , &c ) ;
p r i n t f ( ”The numbers a r e %d , %d , and %d . \ n” , a , b , c ) ;
f f l u s h ( stdout ) ;
p r i n t f ( ” P r e s s ENTER t o e x i t . \ n” ) ;
f f l u s h ( stdout ) ;
getchar ( ) ;
getchar ( ) ;
return 0 ;
}
Observat, ie! Avem nevoie de două instruct, iuni getchar deoarece atunci
când scanf cites, te un număr, caracterul ENTER nu este citit s, i rămâne ı̂n
bufferul de intrare.
O altă variantă pentru blocarea terminalului este cea de mai jos. Aceasta
furnizează o solut, ie generică.
#include <s t d i o . h>
int main ( )
{
int a , b , c ;
p r i n t f ( ” Enter t h r e e i n t e g e r numbers : ” ) ;
f f l u s h ( stdout ) ;
s c a n f ( ”%d %d %d” , &a , &b , &c ) ;
p r i n t f ( ”The numbers a r e %d , %d , and %d . \ n” , a , b , c ) ;
f f l u s h ( stdout ) ;
16
p r i n t f ( ” P r e s s ENTER t o e x i t . \ n” ) ;
f f l u s h ( stdout ) ;
while ( g e t c h a r ( ) != ’ \n ’ )
{
}
getchar ( ) ;
return 0 ;
}
6.7 Exercit, iu
Scriet, i un program care calculează media aritmetică a trei numere introduse de
la tastatură.
6.8 Exercit, iu
Scriet, i un program care cites, te 5 caractere de la tastatură s, i afis, ează codul
ASCII corespunzător fiecăruia dintre ele. Scriet, i două variante pentru program,
una care foloses, te scanf s, i una care foloses, te getchar.
6.9 Exemplu
Program pentru aflarea numărului de electroni care generează o anumită sarcină,
folosind notat, ia s, tiint, ifică.
#include <s t d i o . h>
int main ( )
{
double c h a r g e = 0 ;
double chargeE = −1.602 e −19;
double e l e c t r o n s = 0 ;
p r i n t f ( ” Enter t h e c h a r g e : ” ) ;
f f l u s h ( stdout ) ;
s c a n f ( ”%l f ” , &c h a r g e ) ;
e l e c t r o n s = c h a r g e / chargeE ;
p r i n t f ( ” E l e c t r o n number ( s c i e n t i f i c format ) : %l e . \ n” , e l e c t r o n s ) ;
p r i n t f ( ” E l e c t r o n number : %l f . \ n” , e l e c t r o n s ) ;
f f l u s h ( stdout ) ;
17
p r i n t f ( ” P r e s s ENTER t o e x i t . \ n” ) ;
f f l u s h ( stdout ) ;
while ( g e t c h a r ( ) != ’ \n ’ )
{
}
getchar ( ) ;
return 0 ;
}
6.10 Exercit, iu
Scriet, i un program pentru a calcula s, i afis, a produsul a trei numere ı̂ntregi citite
de la tastatură.
6.11 Exercit, iu
Scriet, i un program pentru a calcula suma, diferent, a s, i produsul unei perechi de
numere reale.
18