Coordonatori
PRACTICA DEZVOLTĂRII
SOFTWARE
ÎN
LIMBAJE DE ASAMBLARE
Editura Economică
Bucureşti 2002
400
Colectivul de autori
Ilie ADRIAN
Laurenţiu ARICIU
Doru CAZAN
Cristian CODREANU
Valentin DRAGOMIR
Ion IVAN
Laur IVAN
Alexandru LEAU
Adrian LICURICEANU
Zsolt MARK
Teodor MIHAI
Paul POCATILU
Mihai POPESCU
Gabriel ŞUTAC
Sebastian TCACIUC
Daniel VERNIŞ
401
CUPRINS
1 Introducere 11
2 Caracteristicile limbajelor de asamblare 13
2.1 Prelucrări elementare 13
2.2 Caracterul neimplicit al definirilor si prelucrărilor 14
2.3 Secvenţialitatea instrucţiunilor 16
2.4 Absenţa priorităţilor 17
2.5 Contextul dinamic 19
2.6 Libera poziţionare 20
2.7 Adresarea generalizată 21
2.8 Necompactitatea construcţiilor 23
2.9 Incluziunea 24
2.10 Concluzii 25
3 Reprezentarea informaţiei 27
3.1 Codificarea informaţiei 27
3.2 Organizarea datelor 28
3.3 Reprezentarea informaţiei numerice 30
3.4 Reprezentări ale şirurilor de caractere 36
4 Moduri de adresare 39
4.1 Calculul adresei unui operand 39
4.2 Modul de adresare imediată 41
4.3 Modul de adresare registru 43
4.4 Adresarea directă 44
4.5 Adresarea indexată 45
4.6 Adresarea bazată 47
4.7 Adresarea bazată şi indexată 48
402
4.8 Adresarea indirectă 49
5 Indicatorii de condiţie 53
5.1 Registrul FLAGS 53
5.2 Operaţii cu indicatorii de condiţie 55
5.3 Poziţionarea indicatorilor de condiţie la execuţie 56
5.4 Interacţiunea indicatori de condiţie - instrucţiuni 56
6 Instrucţiuni 61
6.1 Clasificarea instrucţiunilor 61
6.2 Descrierea instrucţiunilor 63
6.3 Forma externă a instrucţiunilor 65
6.4 Forma internă a instrucţiunilor 71
6.5 Comentariile 77
6.6 Efectele execuţiei instrucţiunilor 78
7 Definirea structurilor de date 81
7.1 Date elementare 81
7.2 Masive unidimensionale 82
7.3 Masive bidimensionale 82
7.4 Articolul 84
7.5 Variabile pointer 88
7.6 Variabile enumerative 89
7.7 Tabele de date 90
7.8 Obiecte 91
8 Implementarea structurilor fundamentale 95
8.1 Programarea structurată 95
8.2 Structura liniară 95
8.3 Structura alternativă 96
8.4 Structura repetitivă standard 99
8.5 Structura repetitivă condiţionată posterior 102
8.6 Structura alternativă multiplă 103
9 Aritmetici binare 107
9.1 Aritmetica binară pe 8 biţi 107
9.2 Aritmetica binară pe 16 biţi 109
9.3 Aritmetica binară pe 32 biţi 112
10 Aritmetici zecimale 117
10.1 Ajustări 117
10.2 Adunarea şi scăderea 119
10.3 Înmulţirea şi împărţirea 120
10.4 Proceduri de calcul 122
10.5 Determinarea lungimii rezultatului evaluări unei expresii aritmetice 129
11 Întreruperi 131
11.1 Întreruperi interne şi externe 131
11.2 Modul de funcţionare al întreruperilor 131
11.3 Tipuri de întreruperi 134
11.4 Exemplu de folosire al întreruperilor 138
11.5 Concluzii 145
12 Macrodefiniţii 147
12.1 Structura macrodefiniţiei 147
403
12.2 Macroapelul şi macroexpandarea 149
12.3 Etichete locale 151
12.4 Variabile locale 153
12.5 Macrodefiniţii derivate 155
12.6 Macrodefiniţii recursive 156
12.7 Redefinirea macrodefiniţiilor 157
12.8 Macrodefiniţii uzuale 157
12.9 Concluzii 163
13 Proceduri 165
13.1 Reutilizabilitatea 165
13.2 Structura unei proceduri 166
13.3 Apelarea procedurilor 168
13.4 Locul procedurilor 172
13.5 Instrucţiuni specifice lucrului cu proceduri 173
14 Prelucrări în virgulă mobilă 179
14.1 Scurtă istorie a prelucrărilor în virgulă mobilă 179
14.2 Resurse 179
14.3 Setul de instrucţiuni 182
14.4 Sintaxa instrucţiunilor 187
14.5 Forma externă a instrucţiunilor 189
14.6 Exemple 190
14.7 Concluzii 193
15 Lucrul cu şiruri de caractere 195
15.1 Caracteristicile instrucţiunilor de lucru cu şiruri de caractere 195
15.2 Setul de instrucţiuni pentru lucrul cu şiruri de caractere 196
15.3 Lucrul cu şiruri de caractere fără a folosi instrucţiuni specializate 198
15.4 Prefixul REP 199
15.5 Compararea a două şiruri 201
15.6 Poziţia unui subşir intr-un şir 203
15.7 Eliminarea spatiilor 204
15.8 Copierea unui şir sursă în şir destinaţie 205
15.9 Stabilirea lungimii unui şir 205
15.10 Conversie de la întreg binar la hexazecimal ca şir de caractere 206
15.11 Înlocuirea apariţiilor unui caracter 207
15.12 Concatenarea a două şiruri 207
15.13 Iniţializarea unui şir 209
15.14 Copierea unui şir dintr-o zonă de memorie într-o altă zonă de memorie 210
15.15 Numărarea caracterelor identice cu un caracter specificat 211
16 Fişiere 213
16.1 Fişiere, articole, identificatori 213
16.2 Operaţii cu fişiere 215
16.3 Utilizarea de fişiere pentru calculul fondului de salarii 221
16.4 Operaţii cu directori si subdirectori 231
17 Programarea mixtă C - limbaj de asamblare 235
17.1 Programul principal este scris în limbaj de asamblare, 235
procedurile apelate sunt scrise în limbajul C
404
17.2 Programul principal este scris în limbajul C, 242
procedurile apelate sunt scrise în limbaj de asamblare
17.3 Concluzii 251
18 Dezvoltarea de aplicaţii orientate obiect in limbaj de asamblare 253
18.1 Concepte folosite în dezvoltarea programelor orientate obiect 253
18.2 Definirea obiectelor prin structuri 257
18.3 Definirea obiectelor prin macrodefiniţii 260
18.4 Folosirea specificaţiilor proprii limbajului de asamblare 276
18.5 Analiza comparativă a variantelor de implementare a obiectelor 282
19 Structuri de programe 289
19.1 Programul ca singură secvenţă (0, 0, 1) 289
19.2 Programul din secvenţe distincte grupate într-un segment (1, 1, 1) 296
19.3 Proceduri incluse în segmentul programului principal (1, 1, 1) 300
19.4 Definiri distincte pentru toate componentele (m, k, n) 304
19.5 Structuri liniare de program 306
19.6 Structura arborescentă 307
19.7 Structuri de tip reţea 316
19.8 Concluzii 318
20 Optimizarea programelor 319
20.1 Criterii de optim 319
20.2 Cicluri maşină 320
20.3 Volumul de operaţii 323
20.4 Secvenţe echivalente 327
20.5 Alegerea tipului de dată 329
20.6 Eliminarea subexpresiilor comune 330
20.7 Gestionarea corectă a invarianţilor 331
20.8 Regruparea ciclurilor 332
20.9 Eliminarea secvenţelor inconsistente 334
20.10 Eliminarea secvenţelor inactive 334
20.11 Reacoperirea segmentelor 335
20.12 Alocarea optimă a regiştrilor 336
20.13 Concluzii 338
21 Designul limbajelor de asamblare 339
21.1 Cerinţe ale designului 339
21.2 Structura instrucţiunii limbajului de asamblare 340
21.3 Designul corectiv pentru limbajele de asamblare 342
21.4 Stabilirea numărului de registre 344
21.5 Concluzii 345
22 Elemente de grafica 347
22.1 Istoric al adaptoarelor grafice 347
22.2 Rutine BIOS şi moduri grafice 349
22.3 Lucrul în mod grafic folosind un adaptor VGA 351
22.4 Lucrul în mod grafic folosind un adaptor SVGA 359
22.5 Concluzii 364
23 Programe rezidente 365
23.1 Caracteristicile programelor rezidente 365
23.2 Întreruperi pentru programe rezidente 366
405
23.3 Sistemul de operare MS-DOS 369
23.4 Resurse ale programelor rezidente 370
23.5 Controlul proceselor 373
23.6 Probleme specifice în realizarea programelor rezidente 378
23.7 Activarea programelor rezidente 385
23.8 Program rezident pentru afişarea ceasului 388
23.9 Concluzii 398
24 Programarea în modul protejat 399
24.1 Moduri de operare ale procesorului 80x86 399
24.2 Regiştrii procesoarelor i386/i486 400
24.3 Moduri de gestiune a memoriei 407
24.4 Comutarea in modul protejat 409
24.5 Implementarea modului de lucru multitasking 423
24.6 Concluzii 426
25 Programarea aplicaţiilor Windows în limbaj de asamblare 427
25.1 Interfaţa de programare a aplicaţiilor Windows 427
25.2 Organizarea memoriei in Windows 9x 428
25.3 Încărcarea programelor Win32 430
25.4 Structura unui program în limbaj de asamblare 430
25.5 Programarea sub Windows 432
25.6 Asamblarea si editarea de legături 441
25.7 Exemplu de program Win32 441
Bibliografie 447
Anexe
A I Arhitectura procesoarelor din familia Intel 80x86 A-1
A II Mnemonicele instrucţiunilor procesorului 80x86 A-5
A III Setul de instrucţiuni ale procesoarelor Intel 80x86 A-11
A IV Regiştrii coprocesorului 80x87 A-69
A V Mnemonicele instrucţiunilor coprocesorului 80x87 A-73
A VI Primul program în limbaj de asamblare A-77
A VII Analiza comparată a utilizării diferitelor tipuri de reprezentări A-89
A VIII Calcul matriceal - studiu de caz A-101
406
2
CARACTERISTICI ALE LIMBAJELOR DE
ASAMBLARE
2.1 Prelucrări elementare
add O1, O2
mov O1, O2
efectuează copierea operandului O2. După executarea ei, operandul O1 are acelaşi
conţinut cu cel al operandului O2.
Instrucţiunea
shl O1,x
xchg O1, O2
Tabelul 2.1.
Limbajul Limbaj asamblare
microprocesor
ASSEMBLER ASSIRIS MACRO-11
486 PENTIUM II
Instrucţiunea
Adunare add ad4, add add add add
Scădere sub sb4, sbd sub sub sub
Înmulţire mul mp4, mpd mul mul mul
Împărţire div dv4, dvd div div div
Comparare cmp cp4, cp1 cmp cmp cmp
Salt jmp bru br jmp jmp
necondiţionat
Salt condiţionat jz, je, jne,... bcf, bz, ... beq, bne,... jz, je, jne,... jz, je, jne,...
add a, b
cei doi operanzi sunt trataţi ca întregi binari. Dacă programatorul nu a inclus în
program procedura de realizare a conversiei operandului B această operaţie nu este
implicită, deci nu se efectuează.
Într-un limbaj algoritmic, dacă operandul de tip întreg A este iniţializat cu
27, iar operandul B de tip zecimal împachetat este iniţializat cu 13, instrucţiunea:
C=A+B
00 13 00 0D
B Temp B
00 1B 00 0D
A + Temp B
00 28
C
409
A dw 27
B dw 0013h
C dw ?
mov ax, A
add ax, B
mov C, ax
I1
I2
JMP A
B: I3
I4
A: I5
I6
I7
JZ B
I8
I1 I2 A1 A2 A3 I3 I4 B1 B2 I5 I6.
I1
I2
call A
I3
I4
call B
I5
I6
mov ax,4C00h
int 21h
A proc
A1
A2
A3
Ret
endp
B proc
B1
B2
ret
endp
I1 I2 J A1 A2 A3 J I3 I4 J B1 B2 J I5 I6
a--*++b-c
411
se evaluează astfel:
se incrementează b;
se înmulţeşte a cu b;
se scade c;
se decrementează a.
Absenţa priorităţilor operatorilor în limbajele de asamblare determină ca
ordinea de efectuare a operaţiilor să fie cea indicată de succesiunea instrucţiunilor
în program. Astfel, dacă se consideră expresia:
a*b+c*d
şi se construieşte secvenţa:
mov ax,a
mul b
add c
mul d
((a*b)+c)*d
mov ax,a
mul b
mov e,ax
mov ax,c
mul d
add e,ax
sau:
(e=(e=(a*b))+(c*d))
a dw 80
În instrucţiunea
add a, 2
413
add ax,[a]
operandul a este o variabilă pointer care indică adresa unde se află operandul care
participă ca termen la efectuarea adunării.
În instrucţiunea
cmp a+1,’c’
.data
x dw 3
y dw 5
z dw ?
.stack
dw 1000 dup (?)
.code
...
mov ax,x
add ax,y
mov z,dx
.data
.stack
414
x dw 3
y dw 5
z dw ?
.code
...
mov bx, bp
mov ax, [bx]
add ax, [bx+2]
mov [bx+4], ax
.code
mov ax,@code
mov ds,ax
jmp alfa
x dw 3
y dw 5
z dw ?
alfa:
mov ax,a
add ax,y
mov z,ax
În definirea:
.data
a dw 300 dup (?)
b dw 100 dup (?)
...
mov ax,a+200
mov bx,b-400
b-400
alfa++
atunci instrucţiunile:
inc b
sau
add b, 01h
mov ax,7
se structurează astfel:
b b+1 b+2
00 07
cod operaţie
Figura 2.6 – Structura instrucţiunii mov ax,7
modifică valoarea conţinută de acest bait, iar rezultatul adunării este 25 (10+15) şi
nu 17 (10+7).
m = min(a,b,c)
m=a
if (mb) m=b;
if (mc) m=c;
...
mov ax,a
cmp ax,b
jle e1
mov ax,b
e1: cmp ax,c
jle e2
mov ax,c
e2 mov m,ax
...
2.9 Incluziunea
struct REGPACK
{
unsigned r_ax, r_bx, r_cx, r_dx;
unsigned r_bp, r_si, r_di;
unsigned r_ds, r_es, r_flags;
};
int main(void)
{
char directory[80];
struct REGPACK reg;
void main()
{
unsigned char a=10, b=11, c;
asm {
mov al,a
add al,b
mov c,al
}
printf("\nc=%d\n",c);
}
2.10 Concluzii
3
REPREZENTAREA INFORMAŢIEI
Chiar dacă un computer stochează diverse tipuri de informaţie (texte,
imagini, sunete), această informaţie este reprezentată intern ca informaţie numerică.
Aceasta face ca o importanţă deosebită să fie acordată reprezentării şi prelucrării
acestui tip de informaţie.
Tabelul 3.1.
Binar Octal Hexazecimal Zecimal
0000 0 0 0
0001 1 1 1
0010 2 2 2
0011 3 3 3
0100 4 4 4
0101 5 5 5
0110 6 6 6
0111 7 7 7
1000 10 8 8
1001 11 9 9
1010 12 A 10
1011 13 B 11
1100 14 C 12
1101 15 D 13
420
1110 16 E 14
1111 17 F 15
Se observă că cifra 4 participă la formarea numărului de două ori, prima dată având
‘valoarea’ 40.000, iar a doua oară având ‘valoarea’ 40.
O altă observaţie foarte importantă este aceea că ‘valorile’ cifrelor se obţin
înmulţindu-le cu puteri ale bazei (1=100, 10=101, 100=102, 1.000=103, etc.).
Aceasta afirmaţie este valabilă şi în cazul altor sisteme de numeraţie poziţionale,
printre care şi sistemele binar, octal şi hexazecimal. Numărul 10010(2) reprezintă:
O singură cifră binară (‘0’ sau ‘1’) este numită ‘bit’ (prescurtare de la
BInary digiT) atunci când este implementată hardware într-un computer. Deoarece
în memoria computerului informaţia este reprezentată ca o înşiruire de biţi, (‘0’ şi
‘1’), se pune problema localizării unei anumite părţi a informaţiei (de exemplu un
număr care participă la o operaţie de adunare). Pentru a facilita accesul la
informaţie, biţii au fost grupaţi prin convenţie. Modalităţile uzuale de grupare
folosite sunt: un singur bit, câte patru biţi (numiţi ‘nibble’), câte opt biţi (numiţi
‘bait’ sau 'octet'), câte 16 biţi (numiţi ‘cuvânt’), câte 32 biţi (numiţi ‘dublu-
cuvânt’).
3.2.1 Bit
421
Deoarece un singur bit este capabil să reprezinte doar două valori diferite,
există impresia că foarte puţină informaţie poate fi reprezentată cu ajutorul unui
bit. Dar nu este chiar aşa! Se poate reprezenta cu ajutorul unui bit informaţie
privitoare la valori de adevăr (Adevărat sau Fals), la stările unui comutator (Închis
sau Deschis), la sexul unei persoane (Bărbătesc sau Femeiesc), etc.
3.2.2 Nibble
Construcţia nibble este un grup de patru biţi. El prezintă interes numai din
punctul de vedere al codificării numerelor în format BCD (Binary-Coded Decimal)
şi al reprezentării numerelor hexazecimale. Cu ajutorul unui nibble se reprezintă cel
mult 16 valori distincte.
Structura internă a unui nibble este:
b3 b2 b1 b0
3.2.3 Bait
Baitul este cea mai importantă structură de date folosită într-un computer.
Are o lungime de opt biţi, notaţi b0, b1, b2, b3, b4, b5, b6, b7, cel mai semnificativ
fiind bitul b7, iar cel mai puţin semnificativ b0.
b7 b6 b5 b4 b3 b2 b1 b0
3.2.4 Cuvânt
422
Un cuvânt este împărţit în doi baiţi sau patru nibble. Cei doi baiţi din
componenţa unui cuvânt poartă numele de baitul cel mai semnificativ (MSB –
most significant byte) şi baitul cel mai puţin semnificativ (LSB – less significant
byte) format din biţii b7-b0.
Cu ajutorul unui cuvânt sunt reprezentate 216=65536 valori distincte. Cele
mai importante utilizări ale unui cuvânt sunt pentru reprezentarea:
întregilor pe 16 biţi;
adreselor pe 16 biţi;
numerelor ce au nevoie de cel mult 16 biţi;
instrucţiunilor cu operanzi impliciţi.
3.2.5 Dublu-cuvânt
Aşa cum arată şi numele un dublu cuvânt este format prin concatenarea a
două cuvinte. Lungimea unui dublu-cuvânt este de 32 biţi şi stochează
4,294,967,296 valori distincte.
Numerele întregi se reprezintă pe unul sau mai mulţi baiţi. Se notează acest
număr cu n. De obicei numărul n are valoarea 1, 2, sau 4. Există două cazuri
distincte de reprezentări:
- pentru numere întregi fără semn;
- pentru numere cu semn.
423
Tabelul 3.2.
Valoare Reprezentare (n=2)
0 0000 0000 0000 0000
1 0000 0000 0000 0001
2 0000 0000 0000 0010
3 0000 0000 0000 0011
4 0000 0000 0000 0100
64 0000 0000 0100 0000
128 0000 0000 1000 0000
256 0000 0001 0000 0000
1024 0000 0100 0000 0000
Tabelul 3.3.
Valoare Reprezentare (n=2)
0 0000 0000 0000 0000
1 0000 0000 0000 0001
2 0000 0000 0000 0010
3 0000 0000 0000 0011
-1 1111 1111 1111 1111
-2 1111 1111 1111 1110
-3 1111 1111 1111 1101
-4 1111 1111 1111 1100
-5 1111 1111 1111 1011
00 07 00 05 00 04 00 03 00 01
un bait
Figura 3.4 – Reprezentarea zecimală despachetată
Tabelul 3.4.
Caracter Codul hexazecimal ASCII
0 30
1 31
2 32
3 33
4 34
5 35
6 36
7 37
8 38
9 39
00 07 05 04 03 01
un bait
Figura 3.5 – Reprezentarea zecimală împachetată
Tabelul 3.5.
Reprezentare
Număr
semn mii sute zeci unităţi zecimi Sutimi Miimi
4823.493 0 4 8 2 3 4 9 3
-52.3 1 0 0 5 2 3 0 0
2.445 0 0 0 0 2 4 4 5
B7 B6 B5 B4 B3 B2 B1 B0
B15 B14 B13 B12 B11 B10 B9 B8
E0 B22 B21 B20 B19 B18 B17 B16
S E7 E6 E5 E4 E3 E2 E1
B7 B6 B5 B4 B3 B2 B1 B0
B15 B14 B13 B12 B11 B10 B9 B8
B23 B22 B21 B20 B19 B18 B17 B16
B31 B30 B29 B28 B27 B26 B25 B24
B39 B38 B37 B36 B35 B34 B33 B32
B47 B46 B45 B44 B43 B42 B41 B40
E3 E2 E1 E0 B15 B50 B49 B48
S E10 E9 E8 E7 E6 E5 E4
B7 B6 B5 B4 B3 B2 B1 B0
B15 B14 B13 B12 B11 B10 B9 B8
B23 B22 B21 B20 B19 B18 B17 B16
B31 B30 B29 B28 B27 B26 B25 B24
B39 B38 B37 B36 B35 B34 B33 B32
B47 B46 B45 B44 B43 B42 B41 B40
B55 B54 B53 B52 B51 B50 B49 B48
1 B62 B61 B60 B59 B58 B57 B56
E7 E6 E5 E4 E3 E2 E1 E0
S E14 E13 E12 E11 E10 E9 E8
‘abcdEFG’
‘12345678’
‘+-.a()’
sir1 db 15 dup(?)
sir2 db ‘abcdefghijk’
sir3 db 100 dup(‘a’), ‘b’
4
MODURI DE ADRESARE
00F00h + 000Ah=00F0Ah,
unde:
xi – număr întreg, deplasare absolută sau relativă;
yi – coeficient de forma 2k, k=0,1,2,…,m;
n – numărul termenilor care intervin în calcul;
m – are valoarea 15 dacă se lucrează pe 16 biţi
respectiv, 31 dacă se lucrează pe 32 biţi.
d0 Sk d1 d2 d3 d4
0 A B C D x
unde:
a0 – adresa de început a segmentului, conţinută într-un registru
(CS, DS, ES);
ci – coeficientul cu valoare unu dacă tipul de deplasare relativă
i este definit (c1 corespunde deplasării ca bază, c2
corespunde deplasării variabile, c3 corespunde deplasării
baitului asociat unei variabile); dacă c0=c1=c2=c3=0
adresarea este imediată;
xi – deplasarea de tip i; x1 este stocată într-un registru de bază,
x2 este stocată într-un registru index, iar deplasarea x3
este pusă în corespondenţă cu numele unei variabile.
Expresiile de calcul ale adreselor prin valorile diferite ale coeficienţilor c i
determină modurile de adresare.
Modul de adresare este definit ca algoritm de calcul al adresei absolute pe
bază de distanţe ale operanzilor.
Modul de adresare oferă posibilitatea implementării atât a structurilor
fundamentale de date (capacitatea de a referi elemente din matrice, linii din matrice
sau chiar matricea în ansamblul ei) cât şi a structurilor de programare (structura
repetitivă este posibilă numai utilizând registrul de index, iar includerea de structuri
repetitive presupune şi utilizarea unui registru de bază).
Instrucţiunea:
val_200 dw 200
mov ax,100
mov ax,c_100
inc ax 40h
push ax 50h
pop ax 58h
xchg cx,ax 91h
pune în evidenţă faptul că registrele prefixate sunt cele care influenţează structura
codului fiecărei instrucţiuni.
Modul de adresare registru conduce la dezvoltarea în procesul de asamblare
a codurilor de instrucţiuni pe un bait (dacă registrele sunt prefixate) sau pe doi baiţi
pentru combinaţii oarecare de registre.
Unele dezvoltări de coduri sunt prezentate în secvenţa:
mov cs,ax 8e c8
mov ds,ax 8e d8
mov ss,ax 8e d0
shr ah,cl d2 ec
xor ax,ax 33 c0
sub si,cx 2b f1
mov ax, x AX
4F 13
1 2
13 4F
x
Figura 4.2 – La primul pas se face calculul adresei de memorie iar la al doilea pas
conţinutul zonei de memorie este mutat în registrul AX
Secvenţa:
utilizează operanzii alfa, beta, gama, sum, ratia, şir, direct prin numele lor, ştiind că
în procesul de asamblare acestor nume li se asociază deplasări faţă de adresa de
început a segmentului unde sunt definite.
Dacă s-au definit şi s-au alocat zone pentru operanzii din secvenţa:
00a0 a dw 10
00a2 b dw 20
00a4 c dw 30, 40, 10, 20
00ac e dw ?
mov ax, a
add ax, b
add ax, c
add ax, c+6
mov e, ax
mov cx,20
xor si,si
xor ax,ax
ciclu:
add ax, x[si]
inc si
inc si
loop ciclu
arată cum registrul index este folosit ca registru de control pentru gestionarea
ciclurilor.
În expresie poate lipsi deplasarea, caz în care registrul index trebuie pregătit
ca să refere corect elementele, încărcând adresa primului element ce se accesează.
Secvenţa:
mov cx,20
lea si,x
xor ax,ax
ciclu:
add ax,[si]
add si, 2
loop ciclu
438
Secvenţa:
Adresa primului element este reper fix, iar adresa de început a liniei i-1 este
adresă de bază pentru referirea unui element.
În formulă apar două progresii aritmetice, una referă linia, a doua referă
elementul în cadrul liniei. Expresia de adresă creşte în complexitate. Mai mult, în
timp ce un registru operand în expresia de adresă are conţinut crescător, celălalt
poate avea conţinut crescător sau descrescător. Traversarea structurilor complexe se
va efectua în direcţii compuse, ceea ce permite realizarea unor algoritmi de referire
cu grad de generalitate ridicat.
Expresiile de adresă au una din structurile:
bp + di
bx + si
bp [di]
bx [si]
reg_bază + reg_index + deplasare
deplasare reg_bază + reg_index
deplasare [reg_bază] [reg_index].
Secvenţa:
xor bx, bx
xor si, si
xor ax, ax
mov cx, nr_linii
ciclul_1: push cx
mov cx, nr_coloane
ciclul_2 add ax, x [bx] [si]
add si, 2
440
loop ciclul_2
xor si, si
pop cx
mov dx, nr_coloane
shl dx,2
add bx, dx
loop ciclul_1
BX 00 02 AX E5 26
2 3
26 E5
00 01 02 03 04
pregăteşte mai întâi variabilele pointer. Variabila pointer adr_a va conţine adresa
operandului a. Variabila adr_adr_a va conţine adresa variabilei pointer adr_a.
Variabila adr_adr_adr_a conţine adresa variabilei pointer spre pointer spre variabila
a.
Ultima instrucţiune din secvenţă are ca efect încărcarea în registrul BX a
conţinutului zonei de memorie (a) a cărei adresă se află într-o zonă de memorie
(adr_a), a cărei adresă la rândul ei se află într-o zonă de memorie (adr_adr_a), a
cărei adresă se află în zona de memorie adr_adr_adr_a. Cele trei asteriscuri pun în
evidenţă că este vorba de o adresare indirectă cu trei nivele de indirectare. Deci în
structura instrucţiunii, dacă este adresare indirectă, al localizarea operandului se va
specifica dacă acesta este sau nu pointer, pentru a continua sau a întrerupe procesul
de referire.
Adresarea indirectă poate fi simplă, adresare indirectă indexată şi adresare
indirectă bazată. Toate aceste mecanisme de indirectare pot fi implementate în
limbajele evoluate şi cu setul de facilităţi de evaluare a expresiilor de adresă
existent acum la microprocesorul 80x86.
Expresiile de evaluare a adreselor pot fi dezvoltate mult în sensul
specificării momentului în care intervin registrele index sau de bază. Astfel, se
poate proiecta un limbaj de asamblare în care expresia:
** alfa bp si
Mai întâi folosind variabila alfa (acum alfa nu este o deplasare) se găseşte
variabila ce conţine o adresă. La această adresă se adaugă conţinutul zonei de
memorie a cărei adresă s-a aflat în registrul BP, obţinându-se o nouă adresă. Este
adresa unei zone de memorie ce conţine o adresă (al doilea nivel de indirectare). La
această adresă se adaugă conţinutul registrului index şi rezultă adresa operandului
cu care se va lucra de fapt.
Fiecare mod de adresare este însoţit de un număr de cicluri maşină.
Alegerea unui anumit mod este dictată de structurile de date cu care se operează.
Expresia de adresă, ca sumă de un număr de termeni fixat se particularizează
funcţie de codul specificat pe biţii 2, 1, 0 ai baitului MODRM (v. capitolul
Instrucţiuni).
Modurile de adresare trebuie privite ca algoritmi de calcul ai expresiilor de
adresă. Operanzii sunt registre sau numere, în final se obţin adrese ale unor zone de
memorie al căror conţinut este întrebuinţat fie pentru a iniţializa, fie pentru a
modifica (prin adunare, scădere, deplasare, interschimb, conversie) un alt operand,
registru.
Indiferent care este procesorul cu care se lucrează, programatorul în
limbajul de asamblare va cunoaşte restricţiile de utilizare registre şi structurile
posibile ale expresiilor de adresă. Folosirea adecvată a unui mod de adresare se
pune în vedere prin lungimea textului scris şi prin mulţimea de instrucţiuni ce pot fi
înlocuite dacă s-ar folosi un alt mod de adresare, mod care s-ar dovedi mai eficient.
443
5
INDICATORII DE CONDIŢIE
5.1 Registrul FLAGS
15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
-- -- -- -- OF DF IF TF SF ZF -- AF -- PF -- CF
Indicatorii CF, PF, AF, SF, ZF, TF şi OF se mai numesc indicatori de stare;
DF este indicator de control, iar IF indicator de sistem.
Odată cu evoluţia procesoarelor din gama x86, au fost introduşi noi
indicatori. Astfel, la procesoarele 80386, registrul FLAGS este extins la 32 de biţi,
formând noul registru EFLAGS, a cărui structură este reprezentată în figura 5.2.
31 30-05 04 03 02 01 00
PG -- ET TS EM MP PE
Acest registru este accesibil pentru citire şi modificare doar prin intermediul
instrucţiunii MOV, deci prin transferul conţinutului său într-un sau dintr-un registru
de uz general.
STC (SeT Carry flag, setează indicatorul de transport) are ca efect CF=1.
CLC (CLear Carry flag, resetează indicatorul de transport) are ca efect
CF=0.
CMC (CoMplement Carry flag, complementează indicatorul de transport)
are ca efect CF= NOT CF.
STD (SeT Direction flag, setează indicatorul de direcţie) are ca efect DF=1
(contorul de adrese va fi decrementat în operaţiile cu şiruri).
CLD (CLear Direction flag, resetează indicatorul de direcţie) are ca efect
DF=0 (contorul de adrese va fi incrementat în operaţiile cu şiruri).
STI (SeT Interrupt flag, setează indicatorul de întreruperi) are ca efect
IF=1.
CLI (CLear Interrupt flag, resetează indicatorul de întreruperi) are ca efect
IF=0.
LAHF (Load AH from Flags, încarcă AH din registrul de indicatori) are ca
efect transferul indicatorilor SF, ZF, AF, PF, CF în biţii 7,6,4,2, respectiv 0 ai
registrului AH.
SAHF (Store AH into Flags, încarcă registrul de indicatori din AH) are ca
efect transferul biţilor 7,6,4,2 şi 0 ai registrului AH în indicatorii SF, ZF, AF, PF,
respectiv CF.
PUSHF şi POPF stochează, respectiv refac registrul de indicatori în,
respectiv din vârful stivei. Aceste două operaţii sunt folosite atât pentru
conservarea stării procesorului prin salvare, cât şi pentru examinarea acesteia.
Variantele PUSHFD şi POPFD lucrează pe 32 de biţi, dar nu afectează indicatorii
RF şi VM, aflaţi în cuvântul mai semnificativ al registrului extins de indicatori.
Tabelul 5.1
Indicatorul OF SF ZF AF P CF
Instrucţiunea F
AAA -- -- -- TM -- M
AAS -- -- -- TM -- M
AAD -- M M -- M --
AAM -- M M -- M --
DAA -- M M TM M TM
DAS -- M M TM M TM
ADC M M M M M TM
ADD M M M M M M
SBB M M M M M TM
SUB M M M M M M
CMP M M M M M M
CMPS M M M M M M
SCAS M M M M M M
NEG M M M M M M
DEC M M M M M
INC M M M M M
IMUL M -- -- -- -- M
MUL M -- -- -- -- M
RCL/RCR 1 M TM
RCL/RCR cont -- TM
ROL/ROR 1 M M
ROL/ROR cont -- M
SAL/SAR/SHL/SH M M M -- M M
SAL/SAR/SHL/SH -- M M -- M M
SHLD/SHRD -- M M -- M M
BSF/BSR -- -- M -- -- --
BT/BTS/BTR/BTC -- -- -- -- -- M
AND 0 M M -- M 0
OR 0 M M -- M 0
TEST 0 M M -- M 0
XOR 0 M M -- M 0
Pentru o listă completă, care include atât indicatorii de sistem, cât şi alte
instrucţiuni decât cele aritmetice, de rotire şi salt, se poate consulta tabelul 5.2.
Tabelul 5.2
Indicatorul
OF SF ZF AF PF CF TF IF DF NT RF
Instrucţiunea
AAA -- -- -- TM -- M
AAD -- M M -- M --
AAM -- M M -- M --
AAS -- -- -- TM -- M
ADC M M M M M TM
ADD M M M M M M
AND 0 M M -- M 0
ARPL M
BOUND
BSF/BSR -- -- M -- -- --
BT/BTS/BTR/ -- -- -- -- -- M
BTC
CALL
CBW
CLC 0
CLD 0
CLI 0
CLTS
CMC M
CMP M M M M M M
CMPS M M M M M M T
CWD
DAA -- M M TM M TM
DAS -- M M TM M TM
DEC M M M M M
DIV -- -- -- -- -- --
ENTER
ESC
HLT
IDIV -- -- -- -- -- --
IMUL M -- -- -- -- M
IN
INC M M M M M
449
INS T
INT 0 0
INTO T 0 0
Indicatorul
OF SF ZF AF PF CF TF IF DF NT RF
Instrucţiunea
IRET R R R R R R R R R T
Jcond T T T T T
JCXZ
JMP
LAHF
LAR M
LDS/LES/LSS/
LFS/LGS
LEA
LEAVE
LGDT/LIDT/LL
DT/LMSW
LOCK
LODS T
LOOP
LOOPE/LOOP T
NE
LSL M
LTR
MOV
MOV control, -- -- -- -- -- --
debug
MOVS T
MOVSX/MOV
ZX
MUL M -- -- -- -- M
NEG M M M M M M
NOP
NOT
OR 0 M M -- M 0
OUT
OUTS T
POP/POPA
POPF R R R R R R R R R R
PUSH/PUSHA
/PUSHF
RCL/RCR 1 M TM
RCL/RCR -- TM
count
REP/REPE/R
EPNE
RET
ROL/ROR 1 M M
ROL/ROR -- M
count
SAHF R R R R R
450
SAL/SAR/SHL M M M -- M M
/SHR 1
SAL/SAR/SHL -- M M -- M M
/SHR count
SBB M M M M M TM
SCAS M M M M M M T
Indicatorul
OF SF ZF AF PF CF TF IF DF NT RF
Instrucţiunea
SET cond T T T T T
SGDT/SIDT/S
LDT/SMSW
SHLD/SHRD -- M M -- M M
STC 1
STD 1
STI 1
STOS T
STR
SUB M M M M M M
TEST 0 M M -- M 0
VERR/VERR M
W
WAIT
XCHG
XLAT
XOR 0 M M -- M 0
6
INSTRUCŢIUNI
temp=CONTOR;
while(temp){
CF=High_Bit(Operand);
Operand*=2;
temp--;
if(CONTOR= =1)
if(High_Bit(Operand)!=CF)
OF=1;
else
OF=0;
else
OF=CF;
}
unde:
temp - variabilă de lucru;
CONTOR - indică numărul de poziţii cu care se face deplasarea spre
stânga; pentru valori diferite de 1, stocarea se va efectua în
registrul CL;
CF, OF - indicatori de condiţie;
High_Bit - funcţie care returnează valoarea bitului cel mai
semnificativ al operandului.
Această instrucţiune are efectul înmulţirii operandului cu 2 la puterea
CONTOR.
Reprezentarea sub formă grafică a mecanismului unei instrucţiuni este
sugestiv, având însă pierderi de informaţii de detaliu asupra modului în care
decurge realizarea operaţiei. Pentru instrucţiunea
în care Operand este registrul AL, reprezentarea grafică este dată în figura 6.1.
454
7 0
C 0
CF AL
Tabelul 6.1.
Număr cicluri maşină
Mod adresare Exemplu
286 386 486
Reg,1 SHL bx,1 2 3 3
Mem,1 SHL alfa,1 7 7 4
Reg,CL SHL ax,cl 5+n 3 3
Mem,CL SHL alfa,cl 8+n 7 4
Reg,imediat8 SHL bx,5 5+n 3 2
Mem,imediat8 SHL alfa,5 8+n 7 4
zonă de memorie se vor face notaţii adecvate. Restricţiile noului limbaj, de cele
mai multe ori nu se regăsesc în descrierile altor limbaje de asamblare pentru a
uşura învăţarea sau compararea lor.
Se va putea vorbi de o descriere bună a instrucţiunilor dacă:
este prezentată mnemonica şi semnificaţia fiecărei litere;
printr-un text se specifică operaţia pe care o execută fiecare instrucţiune;
se detaliază fiecare formă de adresare a operanzilor admisă şi se
exemplifică efectele pe care execuţia instrucţiunilor le are asupra
operanzilor;
se specifică numărul de cicluri maşină necesare execuţiei fiecărei
variante de definire a expresiei de adresare a operanzilor;
se dau reprezentările la nivel de bit ale dispunerii informaţiilor din linia
sursă, după asamblare;
se precizează poziţionarea indicatorilor de condiţie.
Manualele care descriu limbaje de asamblare îmbină toate modurile de
prezentare pentru a asigura atât rigurozitate, cât şi de a face limbajul de asamblare
mai atractiv, mai uşor de învăţat şi de a utiliza cât mai potrivit instrucţiunile.
Tabelul 6.2.
Mnemonicele limbajelor
Operaţia ASSEMBLER-
ASSIRIS MACRO11 ASM
IBM
Addition A,AR,AD AD4,AD8 ADD ADD
Move MVC,MVI MVZ MVSR,MVSL MOV MOV
Jump B BRU BR JMP
Compare C,CD,CDR,CE CP4,CP2 CMP CMP
Shift SLA,SRA SLAx,SRAx ASL,ASR SAL,SAR
Increment IC2,IC4 INC INC
a equ 15
b equ a + 4*6
c equ a + b
se va instrucţiunea:
457
pentru că NOT 10011b este egal cu 01100b, 01100b AND 10101b este egal cu
00100b, iar 00100b OR 11000b este egal cu 11100b.
La introducerea operatorilor relaţionali este important să se cunoască
valorile numerice cu care sunt puse în corespondenţă valoarea logică “adevărat” şi,
respectiv, valoarea logică “fals”. În cazul asamblorului pentru limbaj ASM
adevărat înseamnă (-1), iar fals înseamnă (0). Operatorii relaţionali sunt: GT (mai
mare ca), GE (mai mare sau egal), EQ (egal cu), NE (neegal), LT (mai mic decât),
LE (mai mic sau egal).
Instrucţiunea:
a db ?
b dw l0 dup (0)
str struc
x dw ?
y db 8 dup (?)
458
str ends
alfa str 20 dup (<>)
a dw 7
b db ‘abcd’
c struc
d db ?
x dd 11 dup (?)
c ends
w c 10 dup ()
a este o variabilă de tip cuvânt, b este variabilă de tip bait, w este variabilă de tip c.
Utilizarea numelui unei variabile înseamnă asocierea cu întreaga zonă de memorie
alocată, 2 baiţi pentru variabila a, 1*4 baiţi pentru variabila b şi 450 baiţi pentru
variabila w. Totuşi, orice variabilă chiar dacă are un tip, este privită ca zonă de
memorie. Există posibilitatea de a face conversii de tip aşa fel încât variabila a
definită iniţial ca un cuvânt să poată fi tratată ca un şir de 2 baiţi aşa cum este
variabila b. Tot astfel se pune problema de a face o conversie ca variabila w să fie
privită fie ca un şir de cuvinte, fie ca un şir de baiţi. Conversia de tip presupune
utilizarea operatorului PTR într-o construcţie de forma:
care realizează conversia de la tipul dat după evaluarea expresiei-2 la tipul cerut de
expresia-1.
aa dd 12345678h
secvenţa:
pune în evidenţă că deşi variabila aa este definită dublu cuvânt se explorează din ea
numai un bait, presupunând că registrul SI conţine deplasamentul lui aa.
Operatorii SEG şi OFFSET returnează adresa de început a segmentului
unde se află operandul, respectiv, deplasarea pe care o are operandul faţă de
începutul segmentului.
Dacă o variabilă xx este definită în segmentul de date SEGDAT,
iniţializarea registrului de segment DS se efectuează prin:
460
Tabelul 6.3.
Priorităţi Operatori
1 () [] LENGTH, MASK, WIDTH
2 . (operatorul de referire membri din structuri)
3 HIGH, LOW
4 +,- (operatori unari)
5 : (specificare segment:offset)
6 OFFSET, PTR, SEG, THIS, TYPE
7 *, /, MOD, SHL, SHR
8 +,- (operatori binari)
9 EQ, NE, GT, GE, LT, LE
10 NOT
11 AND
12 OR, XOR
13 LARGE, SHORT, SMALL
Tabelul 6.4.
Instrucţiune Numărul de apariţii
mov 567
pop 152
cmp 89
push 77
sub 32
ret 28
add 20
jmp 14
call 6
lea 6
xor 4
bit 7 6 5 4 3 2 1 0
c c c c c c c c
mnemonica registru
push ax
dec cx
inc dx
bit 7 6 5 4 3 2 1 0
c c c c c r r r
Tabelul 6.5.
Registrul
Cod
Operaţie pe cuvânt Operaţie pe bait
000 AX AL
001 CX CL
010 DX DL
011 BX BL
100 SP AH
101 BP CH
110 SI DH
111 DI BH
inc si
se va codifica astfel:
bit 7 6 5 4 3 2 1 0
0 1 0 0 0 1 1 0
c C c c c r r r
bit 7 6 5 4 3 2 1 0
c c c c c c d w
bit 7 6 5 4 3 2 1 0
m m R R R r/m
Al doilea bait asociat unei instrucţiuni după asamblare are structura dată în
figura 6.6, unde:
m – reprezintă biţii 7,6 asociaţi modului în care sunt interpretaţi biţii 2, 1, 0
şi cum este definită deplasarea operandului faţă de începutul
segmentului (pe un bait sau pe doi baiţi), tabelul 6.6;
R – ocupă biţii 5 ,4 , 3 şi indică registrul operand;
r/m – stabileşte formula de calcul a adresei operandului aflat în memorie.
465
Tabelul 6.6.
Biţii 7, 6
Deplasarea
Mm
00 Lipseşte din instrucţiune
01 Este definită pe un bait
10 Este definită pe doi baiţi
11 Biţii 2, 1, 0 se interpretează drept cod registru
Tabelul 6.7.
Biţii 2, 1, 0 Formula de calcul a adresei
000 BX + SI + deplasare
001 BX + DI + deplasare
010 BP + SI + deplasare
011 BP + DI + deplasare
100 SI + deplasare
101 DI + deplasare
110 BP + deplasare
111 BX + deplasare
se codifică prin:
ba10h
7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
c c c c c c d w m m R R R r/m
1 0 1 1 1 0 1 0 0 0 0 1 0 0 0 0
unde:
biţii notaţi cccccc (101110) reprezintă codul operaţiei MOV registru,
memorie (R - M);
bitul de direcţie (d=1) arată că registrul este destinaţie;
bitul tipului de operand (w=0) arată că operandul este un bait;
466
T1 - cod operaţie;
T2 - cod operaţie (operanzii sunt prefixaţi);
T3 - cod operaţie registru;
T4 - cod operaţie constantă imediată;
T5 - cod operaţie registru, registru;
T6 - cod operaţie registru, constantă imediată;
T7 - cod operaţie registru, expresie de adresă;
T8 - cod operaţie expresie de adresă;
T9 - cod operaţie expresie de adresă, registru;
T10 - cod operaţie expresie de adresă, constantă imediată.
Tabelul 6.8.
Cod Registru segment
00 ES
01 CS
10 SS
11 DS
Apariţia codului registru segment determină ca o zonă de trei biţi să fie completată
0ss, unde ss reprezintă codul registrului segment. Dacă se impune, codul segment
poate ocupa şi poziţiile 4, 3 din baitul codului operaţie.
Se observă că structura internă a instrucţiunii reprezintă un mod
convenţional de punere în corespondenţă a multitudinii de tipuri de instrucţiuni cu
coduri. Se urmăreşte realizarea de combinaţii unice, neambigue, iar asamblorul şi
dezasamblorul să fie aplicaţii, una transformată a celeilalte.
Trecerea la microprocesorul 386 a condus la noi performanţe. Limbajul de
asamblare a rămas în mare parte nemodificat, iar programatorul poate realiza
construcţii noi, putând integra complet pe cele vechi.
În dinamicile hardware, structura internă a instrucţiunilor este afectată cel
mai frecvent. În cazul microprocesorului 386 se specifică următoarele aspecte:
467
codul operaţiei ocupă doi baiţi, permiţând definirea unor noi variante de
prezentare dar şi deschizând posibilitatea definirii de noi instrucţiuni ca
operaţii sau noi tipuri de instrucţiuni, ca structură de listă de operanzi;
lungimile operanzilor sunt un bait, doi baiţi şi patru baiţi, lucru ce se
reflectă prin informaţiile descriptorilor d şi w;
registrele se codifică pentru trei situaţi: registrele de un bait, registrele
pe 16 biţi şi registrele EAX, ECX, EDX, EBX, ESP. EBP, ESI, EDI pe
32 de biţi, cărora li se asociază coduri tot de trei biţi, pe poziţiile din
baitul MODRM.
biţii r/m iau în considerare toate structurile de expresii de adresă în
varianta lucrului pe 16 biţi, iar separat în varianta lucrului pe 32 de biţi;
factorul de scală pentru adresarea indexată (biţii 7, 6) din al treilea bait
de descriere a instrucţiunii;
registrul index (biţii 5, 4, 3) din al treilea bait;
registrul de bază, următorii biţi, 2, 1, 0;
deplasarea sau constanta imediată se reprezintă în continuare pe zero
(dacă lipseşte) baiţi, pe un bait, pe doi baiţi sau pe patru baiţi;
în cazul instrucţiunilor de salt condiţia de testare este înglobată pe patru
biţi în instrucţiune, primii trei biţi definesc combinaţia, iar bitul al
patrulea când este unu determină complementarea faţă de unu a primilor
trei biţi.
Trecerea de la un procesor la altul devine pentru programatorul în limbaj de
asamblare un exerciţiu de codificare cu amplasarea impusă a câmpurilor într-o
succesiune dată de biţi. Mai apare în plus o completare a listei de instrucţiuni ca
mnemonice, cu descrieri riguroase şi eventual noi resurse (registre) sau noi
caracteristici ale resurselor (lucrul pe 32 de biţi).
Prin studierea comparată a structurilor interne ale instrucţiunilor limbajelor
de asamblare, a frecvenţei de utilizare a instrucţiunilor şi a diversităţii
instrucţiunilor şi expresiilor de adresare, este posibilă proiectare unei codificări
optime, care minimizează lungimea textului generat după asamblare, ca număr de
baiţi.
6.5 Comentariile
Astfel, secvenţa:
Instrucţiunile sunt simple sau complexe fie în raport cu modul în care sunt
scrise pe o linie program, fie în raport cu ceea ce efectuează. Este interesant pentru
programator să cunoască complexitatea în raport cu efectele de prelucrare şi să
folosească eficient tot ceea ce oferă instrucţiunile.
Este cunoscut faptul că după efectuarea de calcule are loc poziţionarea
indicatorilor de condiţie. Neglijarea acestui aspect va conduce la scrierea în mod
inutil a unei instrucţiuni de comparare.
Secvenţa:
mov ax, a
sub ax, b
cmp ax, 0
jle alfa
mov ax, a
sub ax, b
jle alfa
469
7
DEFINIREA STRUCTURILOR DE DATE
7.1 Date elementare
unde:
nume - identificator construit după regulile limbajului;
tip - DB, DD, DQ, DT sau un tip definit de utilizator;
n - numărul de componente ale masivului;
val_j - valoarea obţinută după evaluarea unei expresii cu poziţia j din lista
dacă sunt iniţializate componentele masivului.
În secvenţa:
x db 10 dup (‘a’)
y dw 20 dup (0)
z dd 40 dup (?)
w db 5 dup (3, 4, 5)
u dw 1, 2, 3, 4, 5, 6, 7, 8, 9, 10
unde:
nume - identificator cu care se referă matricea;
tip - descriptor de tip DB, DW, DD, DQ, DT;
n - numărul de linii ale matricei;
472
7 3 3 3 3
7 3 3 3 3
1 2 3 4
1 2 3 4
1 2 3 4
1 2 3 4
5 0 0 9
5 0 0 9
5 0 0 9
5 0 0 9
tipul DT-10). De exemplu, masivul bidimensional ee, având tipul DD, cu 5 linii şi
5 coloane, va ocupa o zonă de memorie de 100 baiţi (4 × 5 × 5).
7.4 Articolul
Articolul este un conglomerat de membri, care sunt fie date elementare, fie
masive, fie alte articole. Tipurile membrilor sunt diferite.
Un articol se defineşte prin:
nume struc
membrul_1
membrul_2
……
membrul_n
nume ends
este definit tipul de date persoana, care este un articol care are în componenţă şase
membri având tipurile DB, DW, DD, DB, respectiv, DW. Lungimea zonei de
memorie ocupată de o variabilă de tip STRUC este dată de lungimea membrilor
care intră în alcătuirea ei. Astfel, o variabilă de tip STRUC persoana va ocupa
30+2+4+1+2=39 baiţi.
În secvenţa:
pers persoana (?)
mat_pers persoana 10 dup (10 dup (?))
vect_pr persoana 20 dup (?)
se definesc:
o variabilă cu numele de tip STRUC persoana, ce ocupă 39 baiţi, şi ai
cărei membri se referă cu operatorul . (punct) ca de exemplu, pers.
nume, pers.vârsta, pers.salariu;
474
se defineşte tipul de dată STRUC maşina având un câmp cu valoare prefixată (care
poate fi modificată printr-o iniţializare într-o variabilă dacă respectiva variabilă
impune). Se defineşte variabila OPEL de tip STRUC maşina ale cărei câmpuri sunt
iniţializate după cum urmează: OPEL.nume = ‘berlina’, OPEL.culoare = ‘ROŞIE’,
OPEL.vechime OPEL.vechime=15.
DACIA este un masiv unidimensional cu două componente de tip STRUC
maşina. Prima componentă are membri iniţializaţi astfel: DACIA.nume = ‘brek’,
DACIA.culoare = ‘ROŞIE’, DACIA. vechime=0. A doua componentă (lungimea
unui articol este 20+5+2=27 baiţi) are membri referiţi prin DACIA+27 cu valorile
iniţializate următoare: DACIA+27.nume=’brek’, DACIA+27.culoare= ‘ROŞIE’,
DACIA+27.vechime=0.
Masivul unidimensional numit DIVERSE are 100 componente de tip STRUC
maşina în care membri culoare şi vechime se iniţializează cu ‘ROŞIE’, respectiv,
valoarea 20.
căsătorit (01), divorţat (10) sau văduv (11). Pregătirea profesională poate fi:
absolvent şcoală generală (001), analfabet (000), absolvent liceu (010), absolvent
şcoală profesională (011), absolvent postliceal (100 ), licenţiat (101), absolvent
colegiu (102), doctor în ştiinţe (110), master in science (111). Persoana poate avea
sau nu carnet de conducere (1 sau 0) poate avea sau nu cazier (1 sau 0). Lucrând la
nivel de câmpuri de biţi cu lungime unu, doi sau trei, se va obţine o importantă
economie de memorie la utilizarea de fişiere pentru colectivităţi foarte mari.
Utilizarea de fişiere inverse vine să mărească viteza de regăsire după combinaţii de
caracteristici date sub forma câmpurilor de biţi.
Articolele definite cu câmpuri de biţi utilizează descriptorul RECORD
astfel:
nume record nume-1:n1, nume-2:n2,…, nume-k:nk
unde: nume reprezintă numele tipului de dată RECORD ce se defineşte;
nume-j - numele câmpului j din structura de câmpuri de biţi;
nj - numărul de biţi care formează câmpul j.
nume-j:nj=expresie-j
construieşte tipul de dată având câmpuri pe biţi repartizaţi după urmează: a1 ocupă
2 biţi, a2 ocupă 4 biţi, iar a3 ocupă 2 biţi.
Se creează vectorul aa cu trei componente, fiecare componentă fiind pe un
bait structurat conform tipului de dată RECORD a. Cele trei elemente ale masivului
unidimensional au iniţializaţi biţii ce corespund câmpului a2 cu valoarea 0011b.
Dacă se lucrează la nivel de cuvânt este importantă poziţionarea câmpurilor
întrucât baiţii rămân unităţi de bază şi câmpurile nu pot fi dispersate în doi baiţi
adiacenţi. Fiecare asamblor poate impune restricţii la lungimea în baiţi a tipului de
dată RECORD. De cele mai multe ori, lungimea câmpurilor n1+n2+…+nk nu poate
depăşi 16 biţi.
Când se lucrează cu astfel de structuri se utilizează operatorul WIDTH care
returnează lungimea ca număr de biţi a unui câmp căruia i-a fost aplicat.
Dacă se construieşte secvenţa:
aaa record b:5, c:1, d:2, e:4, f:2, g:2
bbb aaa (?)
. . . . . . . .
476
mov a1, width bbbb + width bbb.e
Reuniunea de date
nume union
membrul-1
membrul-2
membrul-3
. . . . .
membrul-n
nume ends
unde:
nume - reprezintă un identificator care se asociază tipului de dată UNION;
membrul-j - definire tipul de date j;
Lj - lungimea în baiţi a variabilei de tipul j.
O variabilă de tip UNION nume are o lungime egală cu maximul dintre
lungimile L1, L2, . . ., Ln.
Secvenţa:
salariu union
sala dw 5 dup (0)
salb dw ?
nume db 20 dup ?
salariu ends
477
ss salariu 7 dup ( ? )
baitul 0 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 . . 19
sala(i) 0 0 1 1 2 2 3 3 4 4
salb ? ?
nume(i)0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 … 19
Figura 7.1 – Suprapunerea în structură de tip UNION
a db ?
sir db ‘abcdefgh’
c dw ?
e dd 111
addr_a dw offset a
addr_sir dw offset sir
addr_c dw offset c
addr_e dw offset e
addr_x dw ?
. . . . . . . . . . . . .
x: mov addr_x, offset x
call vector_ptr + bx
unde:
nume - tipul de date enum definit de programator;
val_j - valoarea j pe care o poate avea o variabilă de acest tip, care se pune
în corespondenţă cu j –1;
De exemplu, în secvenţa:
s-au definit tipurile de date culoare enum, zile enum, calificat enum, boolean şi
unitate tot enum. Variabila a este de tip culoare enum, c este un masiv
unidimensional cu 5 componente de tip califcat enum, variabilele d şi e sunt şi
iniţializate.
Lungimea zonei de memorie asociată depinde de lungimea listei val-j şi
poate fi un bait, doi baiţi, patru baiţi. De cele mai multe ori codurile sunt 0, 1, 2, . . .
479
pune în evidenţă că variabila ccc de tip termin ENUM poate lua numai valorile
100, 101 şi 102 şi 103.
defineşte tipul de dată articol TABLE şi variabila de tip articol, numită aaa, care
ocupă 50 baiţi. Tipul de date articol TABLE conţine masivele unidimensionale
nume şi reţineri.
7.8 Obiecte
8
IMPLEMENTAREA STRUCTURILOR
FUNDAMENTALE
8.1 Programarea structurată
cld
mov cx, OFFSET VAR
call prel
mov x, dx
mov y, c1
xor cx, cx
call prel
mov ah, 9
mov dx, OFSET text2
int 21h
int 11h
mov ax, a
cmp ax, b
jg adevărat
fals:
mov maxim, b
484
jmp final
adevărat :
mov maxim, ax
final:
nop
mov ax, a
cmp ax, b
jle fals
adevărat:
mov maxim, ax
jmp final
fals:
mov maxim, b
final:
nop
IF(a>b)
maxim:=a
ELSE
maxim:=b;
cmp ax,0
jge final
mov ah, 9
mov dx, OFFSET text
int 21h ;afişare text
final:
mov ah, 4ch
int 21h
care are două instrucţiuni de salt consecutive, greu de acceptat în programe scrise
în limbaj de asamblare eficiente.
Compunerea structurilor alternative impune realizarea unei liniarizări a
secvenţelor fără a introduce multe instrucţiuni de salt.
Secvenţei:
IF (condiţie_1) THEN
IF (condiţie_2) THEN
secvenţa_1
ELSE
secvenţa_2
ELSE
IF (condiţie_3) THEN
secvenţa_3
ELSE
secvenţa_4;
condiţie_1
salt fals_1
adevărat_1:
condiţie_2
salt fals_2
secvenţa_1
salt final_1
fals_2:
secvenţa_2
salt final_2
fals_1:
condiţie_3
salt fals_3
adevărat_3:
secvenţa_3
salt final_3
fals_3:
secvenţa_4
final_1:
486
nop
final_2:
nop
final_3:
nop
cx:=n
secvenţa
de repetat
cx:=cx-1
nu
cx=0
da
xor si, si
xor di, di
xor ax, ax
mov cx, 15
ciclu:
add ax, x [si]
add si, 2
loop ciclu
mov suma, ax
xor bx, bx
mov cx, NR_LINII
ciclul1:
xor ax, ax
xor si, si
push cx
mov ax, NR_COL
ciclul2:
add ax, mat [bx][si]
add si,2
loop ciclul2
mov sum [di], ax
add di, 2
add bx, NR_COL * 2
pop cx
loop ciclul1
iniţializări
expresia
condiţie
secvenţa de
repetat
modificare
operanzi din
expresia
condiţie
cele două teste conduc spre instrucţiunea cu eticheta final, iar asigurarea repetării
este dată de instrucţiunea jmp ciclu. Controlul numărului de repetări se asigură
atât prin contorul CX cât şi prin valoarea registrului AX. Dacă testul este efectuat
asupra registrului CX şi în secvenţa repetitivă nu se modifică în mod corespunzător
(dacă se porneşte de la zero are loc incrementarea, iar dacă se porneşte de la limita
superioară a intervalului are loc decrementarea) efectul este ciclarea infinită.
În toate cazurile se urmăreşte stabilirea corectă a numărului de repetări.
Testarea incorectă a limitei intervalului va conduce fie la neincluderea în calcule a
unor elemente (primul element dacă se va începe cu limita interioară unu), fie la
includerea unui element din afara intervalului (dacă se începe cu elementul zero şi
se numără n elemente în loc de n-1).
a b daca x 1
a b daca x 2
a * b daca x 3
e
b 1 daca x 4
a 1 daca x 5
0 in celelalte cazuri
cmp x,1
je aduna
cmp x,2
je scade
cmp x, 3
je produs
cmp x, 4
je decrement
cmp x,5
je increment
zero:
mov e, 0
jmp final
aduna:
492
mov ax, a
add ax, b
mov e, ax
jmp final
scade:
mov ax, a
sub ax, b
mov e, ax
jmp final
produs:
mov ax, a
mov bx, b
mul bx
mov e, ax
jmp final
decrement:
mov ax, a
dec ax
mov e, ax
final:
nop
cmp x, 1
jne salt_1
adună:
mov ax, a
add ax, b
jmp final
salt_1:
cmp ax, 2
jne salt_2
scade:
mov ax, a
sub ax, b
jmp final
salt_2:
cmp x, 3
jne salt_3
produs:
mov ax, a
mul b1
jmp final
salt_3:
493
cmp x, 4
jne salt_4
decrement:
mov ax, b
dec ax
jmp final
salt_4:
cmp x ,5
jne salt_5
increment:
mov ax, a
inc ax
jmp final
salt_5:
mov ax, 0
final:
mov e, ax
9
ARITMETICI BINARE
e = a + b
se utilizează secvenţa:
494
a db 20
b db 13
e db ?
mov al, a
add al, b
mov e, al
Regiştrii care participă la aritmetica pe 8 biţi sunt AH, AL, BH, BL, CH, CL, DH
şi DL.
mov ah, 0
sub bl, bl
xor cl, cl
mov ch, cl
x db 25
y db 7
z db ?
mov ch, x
mov dl, y
sub ch, dl
mov z, ch
evaluează expresia:
z=x-y
a db 7
b db 5
c db 8
e dw ?
f dw ?
g dw ?
mov al, a
mov bl, b
mul bl
495
mov e, ax
mov al, a
mul c
mov f, ax
mov al, a
mul 3
mov g, ax
evaluează expresiile :
e = a * b
f = a * c
g = 3 * a
a db 32
b db 4
cat db ?
rest db ?
mov al, a
cbw
div b
mov cat, ah
mov rest, al
evaluează expresia:
cat = [a/b]
rest = a – cat * b
div b
496
fie ca registru:
div cl
sau
div dh
sau
div bl
div 7
sau
div 2
e=a+b
se utilizează secvenţa:
a dw 20
b dw 13
e dw ?
mov ax, a
add ax, b
mov e, ax
mov ax, 0
sub bx, bx
497
xor cx, cx
x dw 25
y dw 7
z dw ?
mov cx, x
mov dx, y
sub cx, dx
mov z, cx
evaluează expresia:
z = x-y
a dw 7
b dw 5
c dw 8
e dd ?
f dd ?
g dd ?
mov ax, a
mov bx, b
mul bx
mov e, ax
mov e+2, dx
mov ax, a
mul c
mov f, ax
mov f+2, dx
mov ax, a
mul 3
mov g, ax
mov g+2, dx
evaluează expresiile :
e = a * b
f = a * c
g = 3 * a
498
a dw 32
b dw 4
cat dw ?
rest dw ?
mov ax, a
cwd
div b
mov cat, ax
mov rest, dx
evaluează expresia:
cat = [a/b]
rest = a – cat * b
div b
fie ca registru:
div cx
sau
div bx
div 7
sau
div 2
dp1 dd A92Fh
dp2 dd 49837h
dpsum dd ?
evaluează expresia:
0 0 0 6 A 9 2 F +
0 0 0 4 9 8 3 7
0 0 0 B 4 1 6 6
sau detaliat:
carry 1+
0 0 0 6+ A92F+
0004 9837
000B 4166
a dd 6A92Fh
b dd 4B837h
c dd ?
mov ax, b
sub ax, a
mov bx, b+2
500
sbb bx, a+2
mov c, ax
mov c+2, bx
evaluează expresia:
c = a - b
0006A92F-
0004B837
0001F0F8
sau detaliat:
0 0 0 6- A92F-
0 0 0 4- B837
carry 1
0001 F0F8
W = x + y +24 – z
x dd <valoare oarecare>
y dd <valoare oarecare>
z dd <valoare oarecare>
w dd ?
mov ax, x
mov dx, x+2
add ax, y
adc dx, y+2
add ax, 24
adc dx, 0
sbb ax, z
sbb dx, z+2
mov w, ax
mov w+2, dx
A = a1 216 + a2
B = b1 216 + b2
a1 216 + a2 X
b1 216 + b2
a1 b2 216 + a2 b2 +
32
a1 b1 2 + a2 b1 216
mov ax, a
mul b
mov rez, ax
mov cx, dx
mov ax, a + 2
mul b
mov bx, dx
add cx, ax
adc bx, 0
mov ax, a
mul b + 2
add cx, ax
adc bx, dx
mov rez + 2, cx
mov cx, 0
adc cx, 0
mov ax, a + 2
mul b + 2
add ax, bx
adc dx, cx
mov rez + 4, ax
mov rez + 6, dx
S=B
C=0
cât timp ( S <= A )
{
C=C+1
S=S+B
}
R=A–(S–B)
a dd <valoare>
b dd <valoare>
c dd ?
r dd ?
s dd ?
. . . . . . . . .
mov ax, offset s
mov word ptr [ax], b
mov word ptr [ax+2], b+2
mov ax, offset c
mov word ptr [ax], 0
mov word ptr [ax+2], 1
iar:
cmp32 s, a
jg et
add32 c, 1
add32 s, b
jmp iar
et:
sub32 s, b
mov ax, offset r
mov word ptr [ax], a
mov word ptr [ax+2], a+2
sub32 r, s
Se compară mai întâi părţile mai semnificative ale celor două valori. Numai
dacă acestea sunt egale se trece la compararea parţilor mai puţin semnificative. În
final indicatorii de condiţie vor fi setaţi corespunzător.
Aceste structuri de cod pot fi folosite atunci când nu se poate face uz de un
coprocesor matematic, iar microprocesorul nu posedă regiştri pe mai mult de 16
biţi. Odată cu apariţia microprocesoarelor pe 32 de biţi, se pot folosi registrele
generale EAX, EBX, ECX, EDX. Aceste metode încă prezintă importanţă pentru
operaţii cu numere de peste 64 de biţi, ele putând fi extinse foarte uşor pe orice
număr de biţi, atunci când se lucrează cu numere foarte mari.
10
ARITMETICI ZECIMALE
10.1 Ajustări
0 1 0 3 0 5 0 8 +
0 6 0 6 0 1 0 0
0 7 0 9 0 6 0 8
0 7 0 5 0 6 +
504
0 8 0 7 0 9
0 F 0 C 0 F
0 3 7 8 5 4 +
0 7 8 5 8 7
0 A F D D B
{
(AL)←(AL)-6
(AH)←(AH)-1
(AF)←1
(CF)←(AF)
(AL)←(AL) & 0Fh
}
Timpi Lungime
Operatori 808x 286 386 486 Bytes
reg,reg 3 2 2 1 2
mem,reg 16+EA 7 7 3 2-4 (W88=24+EA)
reg,mem 9+EA 7 6 2 2-4 (W88=13+EA)
reg,immed 4 3 2 1 3-4
mem,immed 17+EA 7 7 3 3-6 (W88=23+EA)
accum,immed 4 3 2 1 2-3
De exemplu, secvenţa:
termen1 dw 11
termen2 dw 23
506
total dw ?
……………..
mov ax, termen1
add ax, termen2
mov total, ax
……………
Timpi Lungime
Operatori 808x 286 386 486 Bytes
reg,reg 3 2 2 1 2
mem,reg 16+EA 7 6 3 2-4 (W88=24+EA)
reg,mem 9+EA 7 7 2 2-4 (W88=13+EA)
reg,immed 4 3 2 1 3-4
mem,immed 17+EA 7 7 3 3-6 (W88=25+EA)
accum,immed 4 3 2 1 2-3
Astfel, secvenţa:
termen1 dw 7
termen2 dw 15
dif dw ?
……………
mov ax, termen2
sub ax, termen1
mov dif, ax
……………
Timpi Lungime
Operatori 808x 286 386 486 Bytes
reg8 70-77 13 9-14 13-18 2
reg16 118-113 21 9-22 13-26 2
reg32 - - 9-38 13-42 2-4
mem8 (76-83)+EA 16 12-17 13-18 2-4
mem16 (124-139)+EA 24 12-25 13-26 2-4
mem32 - - 12-21 13-42 2-4
Astfel, secvenţa:
factor1 dw 41
factor2 dw 15
produs dd ?
……………..
mov ax, factor1
mul ax, factor2
mov produs, ax
mov produs+2, dx
……………
Acumulatorul este împărţit la sursă binar fără semn. Dacă operatorul sursă
este un octet atunci AX este împărţit de sursă (DX trebuie să fie 0); câtul împărţirii
este pus în AL, iar restul în AH. Dacă operatorul sursă este un cuvânt, atunci
DX:AX este împărţit la sursă; câtul împărţirii este pus în AX, iar restul este pus în
DX.
Timpi Lungime
Operatori 808x 286 386 486 Bytes
reg8 80-90 14 14 16 2
reg16 144-162 22 22 24 2
reg32 - - 38 40 2
mem8 (86-96)+EA 17 17 16 2-4
mem16 (150-168)+EA 25 25 24 2-4 (W88=158-
176+EA)
mem32 - - 41 40 2-4
De exemplu, în secvenţa:
deimpartit dw 41
impartitor dw 15
cat dw ?
rest dw ?
……………..
mov ax, deimpartit
cwd
div impartitor
mov cat, ax
mov rest, dx
……………
Tabelul 10.1.
1 octet cu semn -128…127
1 octet fără semn 0…255
2 octeţi cu semn -32768…32767
2 octeţi fără semn 0…65535
4 octeţi cu semn -2^31…2^31-1
4 octeţi fără semn 0…2^32-1
37 32 34 35 38 39 33 31 30 36 $
x
Figura 10.1 – Imaginea zonei de memorie ocupată de şirul x
07 02 04 05 08 09 03 01 00 06 $
72 45 89 31 06 $
37 32 35 34 31 $
a
Figura 10.4 – Imaginea zonei de memorie ocupată de variabila a
07 02 05 04 01 $
b
Figura 10.5 – Imaginea zonei de memorie ocupată de variabila b
07 25 41 $
c
Figura 10.6 – Imaginea zonei de memorie ocupată de variabila c
Se consideră expresia:
e=(a*b+c)/d–a
ly = max { lx, lc } + 1
11
ÎNTRERUPERI
Tabel 11.1.
Nivelul PIC Numarul Intreruperi Dispozitiv
0 08h Timer (ceasul software)
1 09h Tastatura
2 0Ah Slave 8259
3 0Bh Portul serial secundar (COM2)
4 0Ch Protul serial primar (COM1)
5 0Dh Hard Disk (Fix)
6 0Eh Floppy Disk
7 0Fh Imprimanta pe port paralel
8 070h Ceasul hardware
9 071h Master 8259 Nivel 2
10 072h -
11 073h -
12 074h -
13 075h Coprocesorul numeric
14 076h Hard Disk (Fix)
15 077h -
NMI 02h Paritate Memorie
*datorita faptului că întreruperile NMI sunt tot externe, aceasta linie
a fost inclusa in tabel deşi nu este ataşată controller-ului 8259
Acest exemplu poziţionează cursorul pe linia 10, coloana 12, pe prima pagina
video, apelând funcţia 02h a serviciului 10h. Modul de folosire al întreruperilor
521
Tabel 11.2
Număr întrerupere Semnificaţie (serviciu)
00h Divizare la zero
01h Modul single-step (vezi flag tf)
02h Nemascabil (NMI)
03h Breakpoint
04h Depăsire (overflow) (vezi flaguri of si ov)
05h Tipărire ecran
06h Cod de operaţie nevalid
07h Nu există coprocesor matematic
08h IRQ0 Timer
09h IRQ1 Tastatură
0Ah IRQ2 cascade (trecerea pe controllerul 2 de
întreruperi)
0Bh IRQ3 COM 2/4
..... .....
10h Serviciul video
11h Lista echipamentelor
12h Mărimea memoriei convenţionale
13h Disk I/O
14h Port serial
..... .....
40h Vector către funcţiile int 13h referitoare la
dischetă
41h Vectori către structurile referitoare la hard
discuri
43h Tabela de caractere EGA si VGA
46h Vectori către structurile referitoare la hard
discuri
4ah Alarmă utilizator
67h Funcţii ale managerului de memorie expandată
70h IRQ8 Real Time Clock
..... .....
76h IRQ14 hard disk
77h IRQ15 (rezervat)
Tabel 11.3
Număr întrerupere Semnificaţie (serviciu)
20h* Terminare program
21h Servicii DOS
22h Adresa de revenire din program după terminarea
acestuia
23h Adresa de revenire după terminare cu Control-Break
24h Adresa de revenire după o eroare critică
25h/26h Acces direct la disc
27h* Terminare program cu rămânere rezindent în memorie
28h Procesor neocupat (idle)
29h Acces rapid la ecran
2Eh Executare comandă DOS
2Fh Multiserviciu (DoubleSpace, spooler, control TSR, etc.)
31h Servicii DPMI (folosite sub modul Windows 386Enh)
33h Suport mouse
67h Servicii ale managerului de memorie expandată
(HIMEM.SYS)
*Aceste servicii sunt rămase de la versiunile iniţiale de DOS, existând
servicii DOS mai noi şi mai uşor de folosit care le înlocuiesc.
CODESEG
Start:
mov ax, @data ;initializare registru
mov ds, ax ; de date
mov es, ax ; es = ds
mov [word cs:difference],delay ; stabilire pauza
@@10:
call KeyWaiting
jz @@10 ; asteptare apasare tasta
push ds ; salvare ds
mov ax, 251Ch ; setare vector 1C
mov dx, [timerOfs] ;adresa de segment
initiala
mov ds, [timerSeg] ;offset-ul initial
int 21h ; restaurare serviciu initial
pop ds ; restaurare ds
Exit:
mov ah, 04Ch
mov al, [codIesire]
int 21h
;****************************************
; Incetinire - Procedura apelata de aprox. 18,2/sec
; ****************************************
inProgress DB 0 ; flag
difference DW 0 ; timpul de incetinire
PROC Incetinire
PROC KeyWaiting
push ax ; salvare ax
mov ah, 1 ; functia de
; verificare buffer
int 16h ; serviciu tastatura
; BIOS
pop ax ; restaurare ax
ret ; retur
ENDP KeyWaiting
push ds
mov ax, @data
mov ds, ax
. . . . .
. . . . .
pop ds
iret
inProgress DB 0 ; flag
difference DW 0 ; timpul de incetinire
partea timerului în timpul execuţiei procedurii Incetinire, dacă aceasta durează prea
mult. Astfel, acestă rutină devine reentrantă. Din păcate scrierea unei funcţii
reentrante ridică probleme sulpimentare, chiar rutinele din cadrul BIOS-ului si
DOS-ului nefiind reentrante. Posibilitatea reapelării procedurii Incetinire poate să
ducă la depăşirea stivei dacă procedura durează prea mult, iar lanţul de reapelării
continuă fără oprire. Mai mult, folosirea unor variabile globale face ca execuţia să
ia o întorsătură neprevăzută, o singură instanţă a acestora fiind folosită la toate
apelurile. După cum se observă folosirea variabilei difference, face imposibilă
folosirea rutinei Incetinire în mod reentrant. Soluţia la această problemă (soluţie pe
care o folosesc şi unele funcţii BIOS/DOS, cum ar fi Print Screen) este folosirea
unui flag (în cazul de mai sus inProgress), care să indice dacă o instanţă a rutinei
este deja apelată:
Dacă inProgress este mai mare ca zero, instanţa curentă a apărut ca urmare
a blocării altui apel al rutinei, şi se sare la sfârşitul procedurii. Dacă este zero,
instanţa curentă este singura apelată în acest moment şi se poate continua execuţia
ei. Urmează incrementarea flagului pentru indicarea acestui lucru.
După luarea acestor măsuri, urmează altele: permiterea altor întreruperi (sti)
şi salvarea regiştrilor folosiţi în stivă. Deşi, la prima vedere, instrucţiunea sti pare
suficientă pentru activarea întreruperilor, datorită faptului că întreruperile timerului
sosesc prin intermediul cipului 8259, acest lucru nu este adevărat. Deşi
instrucţiunea sti setează flagul if, permiţând indicarea către procesor a semnalelor
de întrerupere, primirea de către acesta a întreruperilor necesită trimiterea către
portul 8259 (cu valoarea 20h) a unei comenzi end-of-interrupt (EOI):
întreruperile nu pot opri execuţia codului între instrucţiunile mov ss, ax şi mov
sp, offset sfarsitStiva. Dacă acest lucru nu s-ar realiza în mod automat, iar o
întrerupere ar avea loc chiar între cele doua instrucţiuni, codul de tratare apelat va
folosi vechea valoare a deplasamentului împreună cu noua valoare a segmentului
de stiva, o situaţie cu urmări catastrofale. Prin urmare, pentru folosirea acestei
metode de protecţie, orice modificare a unui segment, trebuie urmată de
actualizarea deplasamentului.
Pentru o rutină de tratare a unei întreruperi, declararea unui segment de
stivă locală de 512 baiţi face astfel:
ALIGN
stivaLocala DB 512 dub (0)
sfarsitStiva = $
unde directiva ALIGN este necesară pentru alinierea stivei la un multiplu de cuvânt
procesor. Se declară, apoi, o serie de variabile pentru reţinerea vechilor regiştri:
ssVechi DW 0
spVechi DW 0
axVechi DW 0
mov [cs:ssVechi], ss
mov [cs:spVechi], ss
mov [cs:axVechi], ax
530
mov ax, cs
mov ss, ax
mov sp, offset sfarsitStiva
După cum se observă, o sarcină aparent simplă, cum este cea de folosire a
timer-ului, necesită cunoaşterea profundă a implicaţiilor pe care le implică
utilizarea întreruperilor, nerespectarea regulilor mai sus amintite ducând inevitabil
la terminarea anormală a programului sau, chiar, la o cădere de sistem.
11. 5 Concluzii
12
MACRODEFINIŢII
12.1 Structura macrodefiniţiei
variabila booleană care are valoarea zero Dacă prelucrarea s-a efectuat
corect, respectiv unu în caz contrar.
Parametrii p1, p2, .. pn sunt descrişi în prima linie sursă a macrodefiniţiei,
în care se mai specifica numele macrodefiniţiei, cuvântul cheie MACRO, linie cu
structura:
salvreg macro
aduna macro a, b, s
push ax
mov ax, a
add ax, b
533
mov s, ax
pop ax
endm
unde:
e = a + b + c + d
se fac macroapelurile:
aduna a, b, e
aduna e, c, e
aduna e, d, e
…..
mov a, 1
mov b, 3
mov c, 7
mov x, 13
mov y, 2
mov z, 32
aduna3 a, b, c, rez1
aduna3 x, y, z, rez2
aduna3 0, rez1, rez2, e
…..
…...
mov a, 1
mov b, 3
mov c, 7
mov x, 13
mov y, 2
mov z, 32
push ax
mov ax, a
add ax, b
add ax, c
mov rez1, ax
pop ax
push ax
mov ax, x
add ax, y
add ax, z
mov rez2, ax
pop ax
push ax
mov ax, 0
add ax, rez1
add ax, rez2
mov e, ax
pop ax
…..
535
Textul sursă obţinut este mai lung având un număr de instrucţiuni mai mare
decât textul iniţial. Dacă programul asamblor este înzestrat cu componente de
optimizare, atunci se obţine eliminarea din programul macroexpandat a secvenţelor
de instrucţiuni
pop ax
push ax
Programul principal:
536
...
minim x, y, z, min1
minim w, v, u, min2
minim i, j, k, min3
minim min1, min2, min3, min
...
determină minimul dintr-un şir format din nouă elemente. După macroexpandare,
secvenţa de program va arăta astfel:
...
push ax
mov ax, x
cmp ax, y
jlz et10000
mov ax, y
et10000:
cmp ax, z
jlz et20000
mov ax, z
et20000:
mov min1, ax
pop ax
push ax
mov ax, w
cmp ax, v
jlz et10001
mov ax, v
et10001:
cmp ax, u
jlz et20001
mov ax, u
et20001:
mov min2, ax
pop ax
push ax
mov ax, i
cmp ax, j
jlz et10002
mov ax, j
et10002:
cmp ax, k
jlz et20002
mov ax, k
et20002:
mov min3, ax
pop ax
push ax
mov ax, min1
cmp ax, min2
537
jlz et10003
mov ax, min2
et10003:
cmp ax, min3
jlz et20003
mov ax, min3
et20003:
mov min, ax
pop ax
...
Cele patru cifre ataşate etichetei locale limitează macroapelurile la cel mult
10000 într-un program.
Variabilele locale sunt variabile care sunt cunoscute doar în blocul în care
au fost definite. Se pune problema zonelor de memorie, în care să fie alocate aceste
variabile locale:
segmentul de date global
segmentul de date propriu
stiva
În primul caz se profită de faptul că directivele simplificate de definire a
datelor (.date) şi a codului (.code) pot alterna în cadrul programului. Variabilele
locale sunt mai întâi definite cu directiva LOCAL, pentru a nu fi duplicate în apeluri
succesive, apoi declarate în cadrul segmentului de date .data.
Astfel se defineşte o constantă locală, care poate fi aplicată diferitelor date
transmise ca parametri. Fie aceasta constantă
mask macro x
local masca
.data
masca equ 00001111b
.code
push ax
mov al, x
and al, masca
mov x, al
pop ax
endm
538
şablon struc
var_1 dw ?
var_2 dw ?
şablon ends
var_1
var_2
BP
Se calculează mai întâi e*f, iar rezultatul este depus în var_1 (accesul se
face prin [bp].var_1 ). Rezultatul c*d este depus în var_2 ( acces prin [bp].var_2 ).
539
După ce se face înmulţirea dintre a si b, se scade din ax var_2, după care se adună
la ax var_1.
Sursa poate fi valoare imediată, registru sau zonă de memorie, iar destinaţia
poate fi registru sau memorie, dar nu poate fi AX sau BX (care vor avea valorile
dinainte de intrarea în blocul respectiv).
Împărţirii a două numere îi corespunde macrdefiniţia xdiv. Această
macrodefiniţie ca avea patru parametrii:
x - deîmpărţit (sursă)
y - împărţitor (sursă)
c - cât (destinaţie)
r - rest (destinaţie)
k - test (destinaţie)
Parametrul test va conţine după apelul macrodefiniţiei 0 dacă împărţirea se
putea efectua (împărţitorul diferit de 0) şi 1 dacă împărţitorul este 0 (împărţirea ar fi
invalidă).
mov cx,y
mov c,ax
pop reg
Setzeroall Macro
xor ax, ax
xor bx, bx
xor cx, cx
xor dx, dx
xor si, si
xor di, di
endm
545
este :
Setzero macro a1, a2, a3, a4, a5
mov a1, 0
mov a2, 0
mov a3, 0
mov a4, 0
mov a5, 0
endm
Swap1 macro a, b
push ax
mov al, a
mov ah, b
mov b, al
mov a, ah
pop ax
endm
Strcmp macro
push cx
push di
push si
mov si, ax
mov di, dx
mov cx, bx
et1:
mov al, byte ptr ds:[si]
mov dl, byte ptr ds:[di]
cmp al, dl
jne et2
inc si
inc di
loop et1
mov ax, 0
jmp et4
et2:
jb et3
mov ax, 1
547
jmp et4
et3:
mov ax, -1
et4:
pop si
pop di
pop cx
endm
12.9 Concluzii
13
548
PROCEDURI
13.1 Reutilizabilitatea
Secvenţele repetitive şi secvenţele repetabile sunt două lucruri diferite. Două secvenţe
repetitive sunt legate de implementarea structurii repetitive în una din forme (for, do-while, do-
until) rezolvarea secvenţelor repetabile şi-a găsit diferite soluţii dintre care se enumeră:
plasarea secvenţei în afara modulului principal si referirea ei în diferite puncte ale
acestuia; limbajul COBOL are soluţia elegantă prin utilizarea instrucţiunii
PERFORM; reutilizabilitatea e legată numai de programul respectiv;
definirea de proceduri, ca unităţi program distincte, cu grad de reutilizabilitate extins şi
la alte programe; procedura are o listă de parametri împărţită în trei subliste: sublista
parametrilor ce conţin datele iniţiale care vor fi prelucrate în procedură, sublista ce
conţine rezultatele prelucrării şi sublista de parametri care modul cum a decurs
prelucrarea (starea procedurii); procedura nu returnează un rezultat, sau returnează un
rezultat de tip vid;
definirea de funcţii în care lista de parametri conţine numai date de intrare şi rezultatul
prelucrării este unul singur şi va fi returnat, putând fi utilizat direct ca termen în
evaluarea de expresii; funcţiile se construiesc în vederea unei reutilizări foarte largi.
Procedurile şi funcţiile se constituie în biblioteci şi de aceea programatorul
trebuie să dea dovadă de grijă când le construieşte pentru a le asigura
corectitudinea şi un grad de generalizare ridicat. Fără aceste caracteristici nici o
bibliotecă de subprograme nu este operaţională.
Cu atât mai mult soluţiile în limbaje de asamblare care sunt mult mai diversificate, trebuie
realizate astfel încât gradul de reutilizabilitate să atingă nivele care să justifice efortul de a construi
biblioteci de proceduri.
Pentru realizarea conversiilor atât de frecvent utilizate în programe scrise în limbaj de
asamblare se construiesc proceduri. De asemenea, se construiesc proceduri pentru calcule
matriciale, pentru realizarea unor validări, pentru operaţii de intrare/ieşire pe disc, pentru
combinarea unei secvenţe de apeluri de întreruperi. Tot ce se reutilizează în viitor, e preferabil să fie
proiectat ca o procedură.
Salvare registre
Stocarea rezultatelor
Restaurare registre
ret [n]
NumeProcedura ENDP
date segment
parola db 'parola'
n db n-parola ;lungime parolă
text db 'text secret '
db 12 dup(0)
m db m-text ;lungime text
textcod db 24 dup(),'$'
date ends
stiva segment
db 512h dup(0)
stiva nds
cod segment
ssume cs:cod,ds:date,ss:stiva
start:
mov ax, date
mov ds, ax
call par
mov ax,4c00h
int 21h
551
par proc near
mov cl, m
xor ch, ch
lea di, text
lea si, parola
par1:
mov al, byte ptr[di] ;încarcă
caracterul din text în ax
add al, byte ptr[si] ;adună
caracterul din parolă
add al, cl ;adună poziţia
caracterului
add al,100 ;adună o constantă
mov [di+m], al descarcă caracterul
codificat
inc ah
cmp ah, n
jne par2
xor ax, ax
lea si, parola
dec si
par2:
inc di
inc si
loop par1
ret
par endp
cod ends
end start
call sume
sume PROC
call sume
RET RET
553
Program principal
Salvare registre
Pregătirea transmiterii
parametrilor reali
Prelucrare referind
APEL PROCEDURĂ (call) parametri formali
Restaurare registre
Revenire (RET)
Cazul 1:
parametri p1,p2, ...pn sunt în număr restrâns şi sunt stocaţi în registre;
rezultatele r1,r2, ...rm sunt în număr restrâns şi sunt stocaţi în registre;
informaţia de stare se stochează într-un registru.
În această situaţie, pregătirea transmiterii parametrilor înseamnă iniţializare de registre,
apelul procedurii înseamnă salt către prima instrucţiune executabilă din procedură. În procedură se
salvează registre pe stivă. Referirea parametrilor formali, va însemna utilizarea registrelor. Stocarea
rezultatelor se face în registre. Stocarea stării se va face într-un registru (CX = 0 dacă prelucrarea s-
a efectuat corect, CX = -1 dacă prelucrarea nu s-a efectuat corect, de exemplu). Se returnează
registrele. Se are în vedere ca prin restaurare să nu se distrugă din rezultatele obţinute sau registru
de stare al procedurii.
Parametrii reali sunt registrele care se pregătesc în programul apelator.
Parametrii formali sunt aceleaşi registre pe care le utilizează procedura. Li se spune aşa,
probabil pentru faptul că programatorul când scrie procedura nu are în aceşti parametri valori
efective cu care să lucreze. El numai îşi imaginează. Doar după ce a fost apelată, în registru se
554
găsesc informaţii care fac obiectul prelucrării. Lucrul cu registre este cel mai simplu mod de
transmitere a parametrilor, listele de parametri formali, respectiv, reali, suprapunându-se din toate
punctele de vedere.
Cazul 2:
Conţinutul zonelor de memorie care formează parametri reali se transmit pe stivă,
numărul parametrilor fiind fix; parametri formali sunt zonele din stivă care la
procesarea listei au expresii de referire precis construite; aceasta este transmiterea
parametrilor prin valoare; în procedură se lucrează cu copiile parametrilor reali, aflate
în stivă; rezultatele se depun fie în registre (preferabil), fie pe stivă.
Parametrii reali sunt zone de memorie diferite în programul apelator. Parametri formali
sunt zone ale stivei unde se află copii ale valorilor parametrilor reali. Cele două liste se referă la
zone de memorie diferite care au însă acelaşi conţinut. Este cunoscut cazul procedurii de
interschimb a conţinutului dintre două zone de memorie “a” şi „b”.
A - 15 Stivă
B - 20 [BP + 4] - 15
[BP + 6] - 20
Cazul 3:
Zonele de memorie ale căror conţinut urmează să fie prelucrată în proceduri sunt de
tipuri şi lungimi foarte diferite; este convenabil pe stivă să se transmită adresele
zonelor de memorie care formează lista parametrilor reali se construieşte în acest fel
listă de adrese a parametrilor reali, în procedură, parametri formali sunt de fapt
variabile pointer care permit referirea din procedură a zonelor de memorie diferite în
programul apelator; acesta este transmitere prin adresă.
Procedura de interschimb va realiza corect operaţia dacă pe stivă se vor găsi adresele
zonelor de memorie “a” şi „b”; procedura va lucra direct pe ele si într-adevăr va efectua
interschimbul.
Cazul 4:
Se defineşte un segment de date comune tuturor procedurilor si programului apelator.
Printr-o încărcare corectă a adresei acestui segment se va opera cu parametri reali
definiţi în el atât din programul apelator, cât şi din orice procedură segmentul de date
este comun întregii operaţii. Se realizează transmiterea prin referinţă, variabilele cu
anumiţi identificatori se află în segmentul comun aplicaţiei şi de acolo se utilizează
conţinutul zonei de memorie.
.code .code
start: start:
a1 proc ...
... call b1
ret ...
a1 endp call b2
a2 proc ...
... mov ax, 4c00h
ret int 21h
a2 endp b1 proc
... ...
call a1 …
... ret
call a2 b1 endp
... b2 proc
mov ax, 4c00h ...
int 21h ret
end start b2 endp
a) b)
SP = SP – 2
(SP) = IP ;push IP
IP = IP + D16
unde D16 reprezintă deplasarea între destinaţie şi instrucţiunea ce urmează lui CALL în programul
apelator.
556
Instrucţiunea ret efectuează:
Se consideră programul:
Parametri segment
a dw 10
b dw 15
c dw 21
d dw 37
s dw ?
Parametri ends
Stiva segment
Dw 100h dup (?)
varf label word
Stiva ends
Apelator segment
ASSUME cs:code, ds:parametri, ss:stiva
start:
mov ax, parametri
mov ds, ax
mov ax, stiva
mov ss, ax
mov sp, offset varf
mov ax, offset a
push ax
mov ax, offset b
push ax
mov ax, offset c
push ax
mov ax, offset s
push ax
call NEAR ptr suma
mov ax, s
add ax, d
mov s, ax
mov ax, 4c00h
int 21h
Dacă procedura se află în acelaşi segment şi este apelată indirect instrucţiunea call
realizează:
SP = SP – 2
(SP) = IP
(IP) = EA
SP = SP – 2
(SP) = CS
SP = SP – 2
(SP) = IP
IP = (SP)
SP = SP + 2
CS = (SP)
SP = SP + 2
Dinamica stivei când programul apelator se află în segmentul apelului, iar procedura se află
în segmentul procedurii este:
Parametri segment
a dw 10
b dw 15
558
c dw 21
d dw 37
s dw ?
Parametri ends
Stiva segment
Dw 100h dup (?)
varf label word
Stiva ends
Apelator segment
ASSUME cs:code, ds:parametri, ss:stiva
start:
mov ax, parametri
mov ds, ax
mov ax, stiva
mov ss, ax
mov sp, offset varf
mov ax, offset a
push ax
mov ax, offset b
push ax
mov ax, offset c
push ax
mov ax, offset s
push ax
call far ptr suma
mov ax, s
add ax, d
mov s, ax
mov ax, 4c00h
int 21h
Apelator ends
Procedura segment
ASSUME cs:procedura
suma proc FAR
push bp
mov bp, sp
push ax
xor ax, ax
add ax, [bp+12]
add ax, [bp+10]
add ax, [bp+ 8]
mov [bp+ 6], ax
pop ax
pop bp
ret 8h
suma endp
procedura ends
Dacă programul apelator şi procedura se află în segmente diferite, iar apelul este direct,
instrucţiunea CALL realizează:
SP = SP – 2
(SP) = CS
SP = SP – 2
(SP) = IP
(IP) = (EA)
(CS) = (EA) + 2
ret expresie
care evaluează şi devine o valoare de adunare la registru de stivă (SP) ca parte din instrucţiune
determinând executarea secvenţei:
SP = SP + expresie
IP = (SP)
SP = SP + 2
CS = (SP)
SP = SP +2
SP = SP + expresie.
Aceasta este echivalentă cu scoaterea din stivă a unui număr de baiţi specificat prin
expresie; se foloseşte la eliminare din stivă a parametrilor care au fost puşi înainte de apelul
procedurii.
Făcând modificări asupra conţinutului stivei în procedura apelată este posibilă revenirea în
programul apelator la altă instrucţiune şi chiar la instrucţiuni aflate în segment de cod diferit
segmentului programului apelator. Se recomandă folosirea acestei facilităţi doar pentru protejarea
codului.
560
14
PRELUCRĂRI ÎN VIRGULĂ MOBILĂ
14.1 Scurtă istorie a prelucrărilor în virgulă mobilă
14.2 Resurse
000001101……
E1 M1
E2 M2
dacă E1 = E2 atunci M3 = M1 + M2
dacă E1 1 vedem cu cat şi mărim E3. Z1 reprezintă partea întreagă a
mantisei, în acest caz a lui M3.
Dacă este utilizat un coprocesor, resursele disponibile ale acestuia sunt:
opt registre ST0, ST1, ST2, ST3, ST4, ST5, ST6, ST7; fiecare registru
are o lungime de 10 baiţi;
o structura de baiţi, denumită status word, destinată indicatorilor de
condiţie; astfel, coprocesorul 8087 are o structura de 16 biţi cu
indicatorii de condiţie specificaţi în figura 14.1.
562
Utilizare
Invalid operation
exception
Denormalized
operand exception
Zerodivide exception
Overflow exception
Underflow exception
Precision exception
Interrupt exception
Condition code 0
Condition code 1
Condition code 2
Stack-top pointer
Condition code 3
Busy signal
PVST {0, 1, 2, 3, 4, 5, 6, 7}
Oricare dintre regiştri poate fi vârf de stivă. Dacă PVST este 6, o operaţie
de incrementare conduce ca PVST sa devină 7. Dacă PVST este 7, o
operaţie de incrementare va face ca PVST sa devină 0. În cazul în care
PVST are valoarea 0 şi se efectuează o operaţie de decrementare, PVST
va avea valoarea 7. Regiştrii funcţionează ca stivă circulară.
Coprocesorul accesează operanzi definiţi în memorie cu descriptori
de tip DD, DT sau DQ. Regiştrii coprocesorului sunt construiţi în stivă
(figura 14.2.), efectuarea operaţiilor fiind precedată de operaţii de
563
Utilizare
Reserved
Interrupt enable mask
0 = Interrupts enabled
1 = Interrupts disabled
Precision Control
00 = 24 bits
01 = reserved
10 = 53 bits
11 = 64 bits
Rounding Control
00 = round to nearest or even
01 = round down
10 = round up
11 = truncate
Infinity Control
0 = Projective
1 = Affine
Reserved
respectiv:
PVST := PVST + 1
(ST) := | ST |
(ST) := -(ST)
(ST) := (ST )
ST(1) ST
Figura 14.4 – Separarea ca operanzi distincţi a exponentului şi a părţii
fracţionare
f(x) = 2x – 1
g(x) = arctg(x)
r(x) = tg(x)
(ST) := 2(ST) - 1
Fmnem [[op1][,op2]]
570
sau
Fmnem [[destinaţie][,sursa]]
cu semnificaţia:
De exemplu, în instrucţiunea:
FADD ST(1),ST
avem
ST(1) := ST(1) + ST
FADD ST(i),ST
corespunde operaţiei
ST(i) := ST(i) + ST
iar instrucţiunea
FADD ST,ST(i)
corespunde operaţiei
ST := ST + ST(i)
571
Fmnem [[reg][,numevariabila]]
unde operandul sursa se afla numai în memorie, iar operandul destinaţie se afla în
registru.
Instrucţiunea FADD ST(2), preţ corespunde expresiei:
ST := ST + pret
ST(0) := ST(0)*2ST(1)
14.6 Exemple
mov ax,40h
mov es,ax
mov ax,es:10h
test ax,2
jnz Instalat
mov dx,offset NuInstalat
mov ah,9
int 21h
jmp final
Instalat:
FINIT
FLDC ctrl_87
FLD x1 ;st(0)=x1
FLD x2 ;st(1)=x1 si st(0)=x2
FMUL
FST rezultat ;rezultat=st(0)
final:
mov ax,4c00h
int 21h
end
n xk
ex
= k=1 k!
ST(5
)
ST(6
)
ST(7
)
.model small
.stack 512h
.data
nnr dw 10
xnr dd 5
temp dw ?
rez dd ?
.code
e_la_x proc near
fld xnr ; incarca x din memorie
575
fst st(1) ; pune x in st1
fld1 ; pune 1 in st
fst st(2) ; initializeaza k! cu 1
fst st(4) ; initializeaza suma cu 1
mov cx,1
et: fnop
fld xnr
fmul st(1),st ; x la k
14.7 Concluzii
17
PROGRAMARE MIXTĂ: C – LIMBAJ DE ASAMBLARE
;ASM 1
.model small
.stack 100h
.data
; date externe
extrn c format1:word
extrn c format2:word
extrn c format3:word
extrn c format4:word
extrn c format:word
extrn c a:word
extrn c b:word
extrn c c:word
;date locale
suma dw ?
.code
public _main
; intrarea in program
_main proc
lea ax,format2
call printf c,ax
lea ax,format
lea bx,b
call scanf c,ax,bx
lea ax,format3
call printf c,ax
lea ax,format
lea bx,c
call scanf c,ax,bx
; afisarea rezultatului
lea ax,format4
call printf c,ax,suma
//C1
#include <stdio.h>
#include <conio.h>
char format1[20]="a=";
char format2[20]="b=";
char format3[20]="c=";
char format4[20]="\nsuma=%d\n";
char format[20]="%d";
int a,b,c;
void sablon(void)
{
int i;
clrscr();
578
for(i=0;i<80;i++)
printf("-");
printf("Introduceti datele:\n");
for(i=0;i<80;i++)
printf("-");
printf("\n\n\n");
for(i=0;i<80;i++)
printf("-");
printf("\n");
for(i=0;i<80;i++)
printf("-");
gotoxy(1,4);
}
void refac(void)
{
getch();
clrscr();
}
;ASM 2
.model small
.stack 100h
.data
sir db 'parola',0
sirc db 10 dup(?)
m1 db 10,'Siruri identice',0
m2 db 10,'Siruri diferite',0
m3 db 10,'Dati sirul:',0
.code
public _main
extrn c printf:proc
extrn _stringcmp:proc
_main proc
lea ax,m3
call printf c,ax
xor bx,bx
citire:
mov ah,01h
int 21h
cmp al,0dh
je afisare
mov sirc[bx],al
inc bx
jmp citire
579
afisare:
mov al,0
mov sirc[bx],al
lea ax,sir
lea bx,sirc
push ax
push bx
call _stringcmp
add sp,4
test ax,ax
jnz diferite
lea ax,m1
call printf c,ax
jmp sfarsit
diferite:
lea ax,m2
call printf c,ax
sfarsit:
ret
_main endp
end
//C2
int stringcmp(char* x,char* y)
{
while(*x==*y)
{
if(*x=='\0')
return 0;
x++;
y++;
}
return *x-*y;
}
;ASM 3
.model small
.stack 100h
.data
sir1 db 'mergem la ',0,20 dup(?)
sir2 db 'stadion',0
format db 'Sirul concatenat este : %s',10,13,0
.code
public _main
extrn c printf:proc
580
extrn _stringcat:proc
_main proc
lea ax,sir1
lea bx,sir2
push bx
push ax
call _stringcat
add sp,4
lea bx,format
call printf c,bx,ax
ret
_main endp
end
//C3
#include <conio.h>
char* stringcat(char* s1,const char* s2)
{
char* t=s1;
while(*s1)
s1++;
while(*s1++=*s2++);
return t;
}
char* stringcat(char* s1,char* s2)
{
int i;
for(i=0;s1[i];i++);
for(;s1[i++]=*s2++;);
return s1;
}
;ASM 4
.model small
.stack 100h
.data
format1 db 'Introduceti a,b,optiune(1-min,2-max):',0
format2 db '%d%d%d',0
format3 db 'Functia este: %d',10,13,0
a dw ?
b dw ?
x dw ?
pf dw ?
.code
public _main
extrn _funct:proc
extrn c printf:proc
581
extrn c scanf:proc
_main proc
lea ax,format1
call printf c,ax
lea ax,format2
lea bx,a
lea cx,b
lea dx,x
call scanf c,ax,bx,cx,dx
mov ax,x
push ax
mov dx,1
call _funct
add sp,2
mov pf,ax
call pf c,a,b
lea bx,format3
call printf c,bx,ax
ret
_main endp
end
//C4
int min(int a,int b)
{
return a<b?a:b;
}
int max(int a,int b)
{
return a>b?a:b;
}
int (*funct(int x))()
{
int (*pfunct)();
switch(x)
{
case 1:
pfunct=min;
break;
case 2:
pfunct=max;
break;
}
return pfunct;
}
;ASM 5
.model small
mscanf macro par1,par2
582
lea ax,par1
lea bx,par2
call scanf c,ax,bx
endm
mprintf macro par
lea ax,par
call printf c,ax
endm
.stack 100h
.data
format1 db "Maxim = %d",10,13,0
format2 db "a=",0
format3 db "b=",0
format4 db "c=",0
format5 db "%d",0
a dw ?
b dw ?
c dw ?
rez dw ?
.code
public _main
extrn _max:proc
extrn c scanf:proc
extrn c printf:proc
_main proc
mprintf format2
mscanf format5,a
mprintf format3
mscanf format5,b
mprintf format4
mscanf format5,c
push c
push b
push a
call _max
add sp,6
lea bx,format1
call printf c,bx,ax
ret
_main endp
end
//C5
int max(int a,int b,int c)
{
return a>b?(a>c?a:c):(b>c?b:c);
}
//C6
#include <stdio.h>
#include <conio.h>
extern void afis(void);
void main(void)
{
float v,x[40];
int i,n;
printf("n=");
scanf("%d",&n);
for(i=0;i<n;i++)
{
printf("x[%d]=",i);
scanf("%f",&v);
x[i]=v;
}
clrscr();
for(i=0;i<n;i++)
if((i%23==0)&&(i!=0))
{
afis();
printf("\nx[%d]=%f]",i,x[i]);
}
else
printf("\nx[%d]=%8.2f",i,x[i]);
afis();
}
;ASM 6
; Afisarea unui mesaj, asteptarea apasarii unei taste
; si stergerea ecranului
.model small
.data
sir db 'Pentru a continua apasati orice tasta...','$'
.code
public _afis ; Interfata cu programul C
pozit proc ; Pozitionarea pe linia 24,
coloana 1
mov ah,02h ; Functia BIOS
mov dh,24 ; Linia
mov dl,1 ; Coloana
int 10h ; Apelul intr BIOS 10h (functii grafice)
ret
pozit endp
584
sterg proc ; Stergerea ecranului
mov ah,0
mov al,2
int 10h
ret
sterg endp
_afis proc
call pozit ; Pozitionare pe ecran
mov ax,seg sir ; Afisare mesaj
mov ds,ax
mov dx,offset sir
mov ah,09h
int 21h ; Apel functie DOS afisare text
terminat cu '$'
mov ah,08h ; Asteptarea apasarii unei taste
int 21h
call sterg
ret ; Iesirea catre functia apelatoare
_afis endp
end
//C7
#include <stdio.h>
extern int suma(int,int,int);
void main(void)
{
int a,b,c;
printf("a=");
scanf("%d",&a);
printf("b=");
scanf("%d",&b);
printf("c=");
scanf("%d",&c);
printf("suma=%d\n",suma(a,b,c));
}
;ASM 7
.model small
.code
public _suma
_suma proc
push bp ; Salvarea registrului bp
mov bp,sp ; bp<-sp
mov ax,[bp+4] ; ax=par1
add ax,[bp+6] ; ax+=par2
add ax,[bp+8] ; ax+=par3
585
pop bp ; Refacere bp
ret ; Iesirea din functie
_suma endp
end
//C8
#include <stdio.h>
extern int suma(int*,int*,int*);
void main(void)
{
int *pa,a,*pb,b,*pc,c;
printf("a=");scanf("%d",&a);
printf("b=");scanf("%d",&b);
printf("c=");scanf("%d",&c);
pa=&a;pb=&b;pc=&c;
printf("suma=%d\n",suma(pa,pb,pc));
}
;ASM 8
.model small
.stack 100h
.code
public _suma
_suma proc
push bp ; Salvare bp
mov bp,sp ; bp<-sp
mov di,[bp+4] ; di=pa
mov ax,[di] ; ax=*pa;
mov di,[bp+6] ; di=pb
add ax,[di] ; ax+=*pb
mov di,[bp+8] ; di=pc
add ax,[di] ; ax+=*pc
pop bp ; Refacere bp
ret ; Iesire din functie
_suma endp
end
//C9
#include <stdio.h>
extern int* suma(int*,int*,int*);
void main(void)
{
int *pa,a,*pb,b,*pc,c,*ps;
printf("a=");
scanf("%d",&a);
586
printf("b=");
scanf("%d",&b);
printf("c=");
scanf("%d",&c);
pa=&a;
pb=&b;
pc=&c;
ps=suma(pa,pb,pc);
printf("suma=%d\n",*ps);
}
;ASM 9
.model small
.stack 100h
.data
rez dw ?
.code
public _suma
_suma proc
push bp
mov bp,sp
mov di,[bp+4]
mov ax,[di]
mov di,[bp+6]
add ax,[di]
mov di,[bp+8]
add ax,[di] ; ax=*par1+*par2+*par3
mov rez,ax ; rez<-ax
mov ax,offset rez ; ax<-&rez
pop bp
ret
_suma endp
end
//C10
#include <stdio.h>
extern int suma(int*,int);
void main(void)
{
int v,x[40];
int i,n;
printf("n=");
scanf("%d",&n);
for(i=0;i<n;i++)
{
printf("x[%d]=",i);
587
scanf("%d",&v);
x[i]=v;
}
printf("Suma elementelor vectorului = %d",suma(x,n));
}
;ASM 10
.model small
.stack 100h
.code
public _suma
_suma proc
push bp
mov bp,sp
mov cx,[bp+6] ; cx <- numarul de elemente
mov si,[bp+4] ; si <- adresa primului element
xor ax,ax ; ax=0
ciclu: ; do{
add ax,[si] ; ax+=x[i];
inc si ; i++
inc si
loop ciclu ; }while(i<n)
pop bp
ret
_suma endp
end
//C11
/*
Produsul scalar a doi vectori.
*/
#include <stdio.h>
extern int prodscalar(int*,int*,int);
void main(void)
{
int x[40],y[40],v,i,n;
printf("n=");
scanf("%d",&n);
for(i=0;i<n;i++)
{
printf("x[%d]=",i);
scanf("%d",&v);
x[i]=v;
printf("y[%d]=",i);
scanf("%d",&v);
y[i]=v;
}
v=prodscalar(x,y,n);
printf("Produsul scalar = %d\n",v);
}
588
;ASM 11
; Produsul scalar a doi vectori
.model small
.stack 100h
.code
public _prodscalar
_prodscalar proc
push bp
mov bp,sp
mov cx,[bp+8] ; Numarul de elemente
mov si,[bp+4] ; Adresa primului vector
mov di,[bp+6] ; Adresa celui de-al doilea vector
xor bx,bx
ciclu: ; Bucla repetata de cx ori
mov ax,[si] ; bx+=[si]*[di];
mul word ptr [di]
add bx,ax
inc si
inc si
inc di
inc di
loop ciclu
mov ax,bx ; ax=bx (rezultatul)
pop bp
ret
_prodscalar endp
end
//C12
/*
Suma elementelor unei matrice.
*/
#include <stdio.h>
#define N 5
extern int suma(int x[][N],int m,int n);
void main(void)
{
int x[3][N],v;
int i,j,m,n;
printf("numarul de linii = ");
scanf("%d",&m);
printf("numarul de coloane = ");
scanf("%d",&n);
for(i=0;i<m;i++)
for(j=0;j<n;j++)
{
printf("x[%d][%d]=",i,j);
scanf("%d",&v);
x[i][j]=v;
}
printf("suma elementelor matricii = %d\n",suma(x,m,n));
589
}
;ASM 12
; Suma elementelor unei matrici
.model small
.stack 100h
.code
c equ 5 ; Numarul de coloane al matricii
public _suma
_suma proc
push bp
mov bp,sp
mov bx,[bp+4] ; Adresa primului element
xor ax,ax ; ax=0
mov dx,[bp+6] ; Numarul de linii
c1:
mov cx,[bp+8] ; Numarul de coloane
xor si,si ; si=0
c2:
add ax,[bx][si] ; ax+=[bx][si]
inc si ; Urmatorul element
inc si
loop c2 ; Mai sunt coloane ?
add bx,c*2 ; Urmatoarea linie
dec dx
jnz c1 ; Mai sunt linii ?
pop bp
ret
_suma endp
end
//C13
/*
Realizarea unei functii specificate de utilizator (min
sau max) asupra a doua numere introduse de la consola.
Se realizeaza cu aplelu unei functii asm care primeste
ca parametru
functia dorita.
*/
#include <stdio.h>
extern int suma(int*,int*,int(*)(int,int),int);
int max(int a,int b)
{
return a>b?a:b;
}
void main(void)
{
int x[40],y[40],n,i,v;
printf("n=");
scanf("%d",&n);
for(i=0;i<n;i++)
590
{
printf("x[%d]=",i);
scanf("%d",&v);
x[i]=v;
printf("y[%d]=",i);
scanf("%d",&v);
y[i]=v;
}
printf("Suma elementelor maxime = %d\n",suma(x,y,max,n));
}
;ASM 13
; Suma elementelor maxime din doi vectori de elemente
; Functia de comparare e transmisa ca parametru
; (o adresa near)
.model small
.stack 100h
.data
rez dw 0
.code
public _suma
_suma proc
push bp
mov bp,sp
mov cx,[bp+10] ; Numarul de elemente
mov di,[bp+6] ; Adresa celui de-al doilea vector
mov si,[bp+4] ; Adresa primului vector
ciclu:
mov ax,[si]
mov bx,[di]
call word ptr [bp+8] c,ax,bx ; Apel functie de comparare
add rez,ax ; rez+=max
inc si ; Urmatoarele elemente din vectori
inc si
inc di
inc di
loop ciclu
mov ax,rez ; ax<-rez
pop bp
ret
_suma endp
end
...
s=0;
for (i=0; i<30000; i++)
for (j=0; j<70000; j++)
for (k=0; k<20000; k++)
if(i= =j)
s+=k*2;
else
s+=k/2;
...
asm {
mov bx, k
mov ax, i
cmp ax, j
jz egal
shr bx
egal: nop
shl bx
mov ax, s
add ax, bx
mov s, ax
}
17.3 Concluzii
592
18
DEZVOLTAREA DE APLICAŢII ORIENTATE
OBIECT IN LIMBAJ DE ASAMBLARE
Clasa
Obiect1 Obiect2
C lasa
de
baza 2
Clasa
derivată Date şi
metode clasa
derivata . . . .
...
.code
adunare_integer proc
push ax
mov ax, [di].nr_int
add ax, word ptr [si]
mov [di].nr_int, ax
pop ax
retn
adunare_integer endp
inmultire_integer proc
push ax
mov ax, [di].nr_int
mul word ptr [si]
mov [di].nr_int, ax
pop ax
retn
inmultire_integer endp
...
Numărul implicat în adunare / înmulţire este trimis prin adresă prin registrul
SI, iar adresa obiectului este trimisă prin registrul DI.
Pentru clasa Complex procedurile sunt:
...
adunare_complex proc
push ax
mov ax, [di].nr_int
add ax, word ptr [si]
mov [di].nr_int, ax
mov ax, [di].nr_complex
598
add ax, word ptr [si+2]
mov [di].nr_complex, ax
pop ax
retn
adunare_complex endp
inmultire_complex proc
.data
int_temp dw 0
.code
push ax
mov ax, [di].nr_complex
mul word ptr [si+2]
mov bx, ax
mov ax, [di].nr_int
mul word ptr [si]
sub ax, bx
mov int_temp, ax
mov ax, [di].nr_complex
mul word ptr [si]
mov bx, ax
mov ax, [di].nr_int
mul word ptr [si+2]
add ax, bx
mov [di].nr_complex, ax
mov ax, int_temp
mov [di].nr_int, ax
pop ax
retn
inmultire_complex endp
...
Numărul complex este format din două date membre, nr_int şi nr_complex,
adunarea şi înmulţirea fiind definite astfel:
(a , a ) (b , b ) (a b , a b )
1 2 1 2 1 1 2 2
(a , a ) (b , b ) (a b a b , a b a b )
1 2 1 2 1 1 2 2 1 2 2 1
...
.data
Integer struc
nr_int dw 0
adun_int dw adunare_integer
599
inm_int dw inmultire_integer
Integer ends
Complex struc
baza Integer <>
nr_complex dw 0
adun_compl dw adunare_complex
inm_compl dw inmultire_complex
Complex ends
...
.code
start:
mov ax,@data
mov ds,ax
lea di,ob_integer
call ob_integer.adun_int ;nr_int = 4+10 = 14
call ob_integer.inm_int ;nr_int = 14*10 = 140
lea di,ob_complex
call ob_complex.adun_compl ;nr_int = 7+10 = 17
;nr_complex = 5+5 = 10
call ob_complex.inm_compl ;nr_int = 17*10-10*5 = 120
;nr_complex = 17*5+10*10 = 185
sfarsit:
mov ax,4c00h
int 21h
end start
setBaseAndNoToZero MACRO id
baseNo&id = 0
No&Id = 0
ENDM
public_ MACRO
currProt = 0
ENDM
private_ MACRO
currProt = 1
ENDM
func nume_rutina
publicExt MACRO b
IRP den,b
publicDer = 1 ;mostenire publica
makeBaseName1 %currId, den
%OUT < Current class is derived from den ... >
ENDM
ENDM
privateExt MACRO b
IRP den,b
publicDer = 0 ;mostenire private
makeBaseName1 %currId, den
%OUT < Current class is derived from den ... >
ENDM
ENDM
makeBaseName1 MACRO currId, den
baseNo&currId = baseNo&currId + 1
makeBaseId currId, %(baseNo&currId), %(den&Id)
makeBaseName2 %(baseNo&currId), currId, %(den&Id), den
ENDM
;macro-uri interne pentru extend
makeBaseId MACRO currId, currBaseNo, baseId ;creeaza
;variabila id a bazei
baseNo&currBaseNo&Of&currId&Id = baseId
ENDM
makeBaseName2 MACRO currBaseNo, currId, baseId, den
606
;aloca obiectul de baza
baseNo&currBaseNo&@base@ den <>
;calculeaza deplasamentul fata de clasa derivata
depl&baseId&In&currId = OFFSET baseNo&currBaseNo&@base@
;mostenire publica
IF publicDer
;declara clasa derivata ca prietena a clasei de baza
friend3 currId, baseId
ENDIF
ENDM
ENDIF
ENDIF
ENDM
operDreapta MACRO oper, unde, id, ob, camp
IFDEF camp&id&@val@ ;se afla direct in ;clasa?
;s-a gasit si se face operatia in functie de protectie
isProt? id, camp
oper unde, id, ob, camp
.model large
include object.asm
.stack 100h
class Integer
public_
date nr_int, dw, 0 ;partea intreaga
func Adunare
func Inmultire
Integer ends
class Complex
610
publicExt <Integer>
public_
date nr_complex, dw, 0 ;partea complexa
func Adunare
func Inmultire
Complex ends
.code
;DEFINIREA METODELOR
classFunc Integer, Adunare
push ax
get ax, nr_int
add ax, word ptr [si]
set nr_int, ax
pop ax
endFunc Integer, Adunare
.data
define ob_integer, Integer
define ob_complex, Complex
numar1 dw 10
numar2 dw 5
.code
start:
mov ax,@data
mov ds,ax
sfarsit:
mov ax,4c00h
int 21h
end start
612
E:\TOOLS\TASM\WORKING>tasm intcomp2
Turbo Assembler Version 4.1 Copyright (c) 1988, 1996
Borland International
Se observă că apar două erori, una pentru set-ul pe clasa Integer, iar alta
pentru cel pentru clasa derivată Complex. Dacă cele două instrucţiuni set se şterg,
procesul de compilare nu va mai semnala nici un fel de eroare, ceea ce
demonstrează faptul că accesarea datelor membre private din cadrul procedurilor de
adunare şi înmulţire sunt corecte datorită apartenenţei acestor rutine la cele două
clase.
După cum se observă, pentru indicarea instanţei curente (cea asupra căreia
se realizează operaţiile) se foloseşte o dată de tip word, currOffset. Aceasta se
încarcă în mod repetat în registrul BX, deoarece între instrucţiunile prezentate pot
exista şi alte instrucţiuni care afectează acest registru. În consecinţă, codul rezultat
din expandarea macroinstrucţiunilor este departe de a fi optim.
O optimizare foarte uşor de făcut, este aceea de a sacrifica un registru (aşa
cum în cazurile anterioare s-a folosit registrul SI), care urmează să ia locul
variabilei currOffset, dar care nu mai poate fi utilizat decât cu precauţie de către
utilizator (trebuie salvat în stiva înainte de blocul de operaţii în care se foloseşte şi
restaurat înainte de folosirea uneia dintre macroinstrucţiuni). Pentru aceasta s-a ales
registrul BX, pentru că acesta oricum se foloseşte la adresarea bazată din cadrul
macrodefiniţiilor set şi get.
;setare valoare
set MACRO ob, camp, v
;verifica daca suntem intr-o functie membra (nu se ;mai
specifica obiectul)
IFB <v>
operStanga movToOb, %curId, curOffset, ob, camp
ELSE
mov bx,OFFSET ob
operStanga movToOb, %(ob&Id), ob, camp, v
ENDIF
IFE gasit ;nu s-a gasit
%OUT < **Error** camp is not a member of ob ... >
.ERR
ELSE
gasit = 0 ;pt. urmatoarele cautari se
;revine la 0
ENDIF
deplasament = 0
ENDM
;incarcare valoare
get MACRO unde, ob, camp
;verifica daca suntem intr-o functie membra (nu se ;mai
specifica obiectul)
IFB <camp>
615
operDreapta movFromOb, unde, %curId, curOffset,
ob
ELSE
mov bx,OFFSET ob
operDreapta movFromOb, unde, %(ob&Id), ob, camp
ENDIF
unde modificator poate fi NEAR sau FAR, însoţit sau nu de directiva GLOBAL.
Declaraţia metodelor clasei are următoarea sintaxă:
DATASEG
b1 Baza <1.1>
b2 Baza < >
Prin folosirea moştenirii se creează clase noi din cele deja existente. Clasa
derivată este o copie a clasei de bază la care se pot adăuga noi metode sau
variabile. Metodele adăugate pot fi complet noi sau le pot înlocui pe cele cu acelaşi
nume în clasa de bază. În plus, metodele din clasa nouă pot apela metodele din
clasa de bază pe care le înlocuiesc. Nu se pot înlocui date ale clasei de bază.
În continuare se prezintă un exemplu de implementare a unei clase derivate
din cea anterioară, în limbaj de asamblare orientat obiect:
GLOBAL Deriv_constructor: PROC
GLOBAL Deriv_actiune: PROC
Inserarea uneia sau mai multor metode virtuale într-o declarare a unei clase
se realizează prin prefaţarea declarării metodei cu cuvântul cheie VIRTUAL. Apoi,
în secţiunea de date a clasei se inserează un pointer la VMT folosind directiva
TBLPTR.
Cuvântul cheie TBLINST declară o instanţă a tabelei metodelor virtuale
(VMT), definind spaţiul în memorie pentru aceasta. Simpla declarare a unei
instanţe VMT nu realizează şi iniţializarea pointerului la VMT pentru diversele
obiecte ale respectivei clase.
DATASEG
TBLINST
DATASEG
d1 Deriv < >
CODESEG
mov si, offset d1
CALL si METHOD Deriv:constructor
CODESEG
mov si, offfset b1
CALL Baza PTR si METHOD Baza:actiune
619
CODESEG
mov si, offset d1
CALL Baza PTR si METHOD Baza:actiune
PROC Deriv_actiune
call Baza_actiune
ret
ENDP Deriv_actiune
IDEAL
JUMPS
LOCALS @@
MODEL large, PASCAL
STACK 1000h
. . . . .
...
GLOBAL Int_Adunare: PROC
GLOBAL Int_Inmultire: PROC
GLOBAL Complex_Adunare: PROC
GLOBAL Complex_Inmultire: PROC
CODESEG
PROC Int_Adunare
ARG @@int:word
USES ax
PROC Int_Inmultire
ARG @@int:word
USES ax
PROC Complex_Adunare
621
ARG @@int:word, @@compl:word
USES ax
PROC Complex_Inmultire
ARG @@int:word, @@compl:word
USES ax, bx
...
DATASEG
ob_integer Integer <4>
ob_complex Complex <7, 5>
CODESEG
start:
mov ax,@data
mov ds,ax
sfarsit:
mov ax,4c00h
int 21h
end start
sfarsit:
mov ax,4c00h
int 21h
end start
sfarsit:
mov ax,4c00h
int 21h
end start
pop cx
loop next1
sfarsit:
mov ax,4c00h
int 21h
end start
Funcţii virtuale
Prima variantă de implementare prezintă cele mai puţine linii de cod şi cel
mai mic timp de execuţie. Acest lucru este explicat prin lipsa celor mai elementare
concepte ale programării orientate obiect (implementarea nu oferă nici măcar o
protecţie a datelor sau polimorfism). Tot ceea ce scrie programatorul se reflectă în
mod identic în codul sursă supus asamblării, singura abatere de la programarea
tradiţională în cod de asamblare o constituie includerea pointerilor către proceduri
în cadrul structurilor.
Cea de-a doua metodă de implementare este prezentată în cele două
variante, observându-se câştigul substanţial de viteză al formei optimizate. Aceasta
se apropie ca performanţă de metoda care foloseşte structuri simple (22 de secunde
de execuţie faţă de 20), oferind în plus programatorului încapsulare, polimorfism şi
moştenire multiplă. Prima variantă este mult mai lentă datorită folosirii unei date
suplimentare pentru deplasare în cadrul structurilor şi a salvărilor repetate în stivă a
registrului BX. Dar, aceasta oferă un grad mai ridicat de încapsulare. Utilizatorul
macroinstrucţiunilor nu este supus nici unor restricţii în folosirea registrului BX,
care în cazul variantei optimizate trebuie salvat şi restaurat de fiecare dată ţinând
cont că şi codul expandat de macrodefiniţii foloseşte acest registru. De asemenea,
varianta a doua, din motive de optimizare a codului, necesită încărcarea explicită în
registrul BX a instanţei asupra căreia se lucrează, ceea ce duce la creşterea
probabilităţilor de apariţie a erorilor.
Ultima variantă de implementare, cea oferită de firma Borland, oferă
performanţe medii şi acest lucru se explică prin modul în care sunt trataţi
parametrii procedurilor. Aceştia sunt transmişi prin stivă, iar pentru folosirea lor se
rezervă regiştri care trebuiesc salvaţi înainte de încărcare şi restauraţi la ieşirea din
procedură. Pentru a urmări codul generat, să luăm procedura de adunare a
numerelor complexe:
PROC Complex_Adunare
ARG @@int:word, @@compl:word
USES ax
19
STRUCTURI DE PROGRAME
O structură de program se memorează în segmente de date (x), segmente de
stivă (y) şi segmente de cod (z) şi este definită prin tripletul (nx, ny ,nz), unde:
nx – numărul de segmente de date;
ny – numărul de segmente de stivă;
nz – numărul de segmente de cod.
În continuare se prezintă tipurile principale de structuri de program atât prin
plasarea în memorie (segmente) cât şi în raport cu raporturile dintre proceduri.
x>0
x+y+z+w y>0
z>0
w>0
F(x,z,y,w)= x<0
x2+y2+z2+w2 y<0
z<0
630
w<0
|x|+|y|+|z|+|w| în rest
POW2 proc
push bp
mov bp, sp
push ax
631
push dx
mov ax, [bp+4]
mul [bp+4]
mov [bp+6], ax
pop dx
pop ax
pop bp
ret
endp
Textul sursă pentru procedura de calcul a modulului unui număr s=|a| este:
modul proc
push bp
mov bp,sp
push ax
mov ax, [bp+4]
neg ax
mov [bp+6], ax
pop ax
pop bp
ret
endp
start:
mov ax, @data
mov ds, ax
cmp x, 0
jle alfa
cmp y, 0
jle alfa
cmp z, 0
jle alfa
mov ax offset fxyz
push ax
mov ax, offset w
push ax
mov ax, offset z
push ax
632
mov ax, offset y
push ax
mov ax, offset x
push ax
jmp suma
alfa:
cmp x, 0
jz beta
cmp y, 0
jz beta
cmp z, 0
jz beta
cmp w, 0
jz beta
mov ax, offset xp
push ax
mov ax, offset x
push ax
call POW2
mov ax, offset yp
push ax
mov ax, offset y
push ax
call POW2
mov ax, offset zp
push ax
call POW2
mov ax, offset wp
push ax
mov ax, offset w
push ax
call POW2
mov ax, offset s
push ax
mov ax, offset wp
push ax
mov ax, offset zp
push ax
mov ax, offset yp
push ax
mov ax, offset xp
push ax
jmp suma
beta:
mov ax, offset mx
push ax
mov ax, offset x
push ax
call modul
mov ax, offset my
push ax
mov ax, offset y
633
push ax
call modul
mov ax, offset mz
push ax
mov ax, offset z
push ax
call modul
mov ax, offset mw
push ax
mov ax, offset w
push ax
call modul
mov ax, offset s
push ax
mov ax, offset w
push ax
mov ax, offset mz
push ax
mov ax, offset my
push ax
mov ax, offset mx
push ax
suma:
call ADD3
...
;procedura pentru conversia de la binar la şir de caractere
;şi reprezentare f(x, y, z, w)
...
mov 4c00h
int 21h
end start
x dw 2
y dw 3
z dw 4
w dw 1
px dw ?
py dw ?
pz dw ?
pw dw ?
mx dw ?
my dw ?
mz dw ?
mw dw ?
s dw ?
start
x>0
y>0
z>0
w>0
x=0
push …
y=0
push …
z=0
push …
w=0
|x|
push …
x2 |y|
y2 |z|
z2 |w|
s=a+b+c+d
w2 push …
conversiepush … push …
push …
afisare push …
push …
push …
stop
push …
635
...
cmp x, 0
jle alfa
cmp y, 0
jle alfa
cmp z, 0
jle alfa
cmp w, 0
jle alfa
add ax, z
add ax,y
add ax,x
mov s, ax
jmp imprimare
alfa: cmp x, 0
jz beta
cmp y, 0
jz beta
cmp z, 0
jz beta
cmp w, 0
jz beta
mov ax, x
mov mx, ax
neg mx
mov ax, y
mov my, ax
neg my
mov ax, z
mov mz, ax
neg z
mov ax, w
mov mw, ax
neg w
mov ax, mx
add ax, my
add ax, mz
add ax, mw
mov s, ax
jmp imprimare
beta: xar bx, bx
636
mov ax, x
mul x
add bx, ax
mov ax, z
mul y
add bx, ax
mov ax, z
mul z
add bx, ax
mov ax, w
mul w
add bx, ax
mov s, ax
...
e=(a+min{xi}+max{xi})*(b+min{yi})-(c+max{wi})-(min{zi}+max{zi})
rând1 struc
filler1 db 10
linii db 60
ends
rând2 struc
filler2 db 10 dup (‘’)
col21 db ‘I ‘
denumire 2 db 30 dup (‘’)
col22 db ‘ , ‘
cant2 db 4 dup (‘’)
col23 db ‘ , ‘
pret2 db 5 dup (‘ ‘)
col24 db ‘ , ‘
639
valoare2 db 8 dup (‘ ‘)
col25 db ‘ ,‘
rând3 struc
filler db 10 dup (‘ ‘)
col31 db ‘, ‘
denumire3 db ’Denumire/30’
col32 db ‘ , ‘
cant3 db ‘CANT’
col33 db ‘ , ‘
pretunit3 db ‘ PU’
col34 db ‘ , ‘
valoare3 db ‘ valoare’
col35 db ‘ ,’
call scrie-rand1
call scrie-rand2
call scrie_rand2
call scrie_rand3
call scrie_rand2
call scrie_rand2
call scrie_rand1
call scrie_rand4 (I0I1I2I3)
call scrie_rand1
F=((A2+B’)*C+D)*G
Vsi:=Vpi+D
unde i=1,NCOL
prodscal proc
mov cx, NCOL-1
mov i,0
mov f,0
mov f+r, 0
ciclu: evw vs,i, poz
mov ax, [poz]
evw g, i, poz
mul [poz]
add f, ax
adc f+2, dx
inc i
loop ciclu
ret
endp
transp proc
push ax
push cx
mov ax, NLIN
mov i, 0
ciclu1:
push cx
mov j, 0
mov cx, NCOL
ciclu2:
emw b, i, j, NCOL, poz
mov ax, [poz]
emw bt, j, i, NLIN, poz
mov [poz], ax
inc j
643
loop ciclu2
pop cx
inc i
loop ciclu 1
pop cx
pop ax
ret
endp
admat proc
push ax
push ax
mov ax, NLIN
mov i, 0
ciclu 1:
push cx
mov j, 0
mov cx, NCOL
ciclu 2:
emw AP, i, j, NCOL, poz
mov ax,[poz]
emw BT, i, j, NCOL, poz
add ax,[poz]
emw MS, i, j NCOL, poz
mov [poz], ax
inc j
loop ciclu 2
pop ax
inc d
loop ciclu 1
ret
endp
call prodmat
call transp
call admat
call prodmv
call advec
call prodscal
call convert
call afisir
mov ax, 4c00h
int 21h
end start
644
A1
A2
A3
A1 A4 Ax+1
A5
A6
A7
f1 f2 fn-1 fn
● ● ● ● ●
Figura 19.3 – Structură liniară de program
Acestei structuri îi corespunde secvenţa de program:
647
...
call f1
call f2
...
call fn
mov ax,4c00h
int 21h
f1 proc
...
ret
endp
f2 proc
...
ret
endp
...
fn proc
...
ret
endp
f0
vi
fi fj
call fi
...
call fj
cmp vi, 0
jz apelj
apeli:
call fi
jmp ...
...
apelj:
call fj
...
În structura arborescentă asociată unui program se găsesc noduri interne
având forma dată în figura 19.5.
fk
vk
649
fi fj fk
f0
Vk=1
f1
Vk=2
f2
Vk=3
f
650
f0
V1=1
V2=1 V2=1
f1 f2 f3 f4 f5 f6 f7 f8
call fi ;i=1, 2, …, 8
jmp final
652
f1 f2 f3 f4 f5 f6 f7 f8
int 21h
f0
V=a1
Vk=a2
Vk=a3
Vk=an
fn+1 fn f3 f2 f1
- prin scanare B3
- concatenare de fişiere B4
- Calcul salarii C
- pentru un salariat C1
- pentru salariaţii unui compartiment C2
- pentru toţi salariaţii C3
- Actualizare D
- modificare salariu brut D1
- modificare impozit D2
- modificare vechime D3
- modificare volum produse D4
- Afişare rezultate E
- pentru un salariat E1
- pentru toţi salariaţii E2
- afişare nume şi salariu net E3
- afişare toate datele E4
B C D E
B1 B2 B3 B4 C1 C2 C3 D1 D2 D2 D4 E1 E2 E3 E4
655
f0
C2=1 C1=2
C3=1
C1=3
f1 f2
C3=2 C5=1
f3
f7
C3=3 C5=2
f4
f8
C5=3
f5 f6
f9
C5=4
f10
f11
f12
657
fk
C=1
N D
fi fj
C1
N D
f1 f2
C2 C3
N D N D
f3 f4 f6
C1:= C1+ f5
658
f1 f2 f3 f4 f5
q1 q2 q3 q4 q5
f1 f2 q3 q4 q5
q1 q2 q3 q4 q5
q1 q2 f3 f4 f5
Mai nou, se construiesc structuri de tip reţea cu reluarea
prelucrărilor de la toate punctele de prelucrare, figura 19.18:
f0 (…(f1 f2 … fj)kr…)kr+1fn
se dezvoltă:
660
f0 f1 f2 f3 f1 f2 f3 f4 f1 f2 f1 f2 f3 f4 f5
19.8 Concluzii
20
OPTIMIZAREA PROGRAMELOR
Optimizarea programelor reprezintă una din direcţiile spre care se
concentrează realizatorii de software. Există numeroase aspecte ale optimizării
programelor: optimizarea timpului de execuţie, optimizarea dimensiunii
operanzilor, optimizarea textului sursă etc. Toate aceste criterii de optimizare,
considerate cu diferite ponderi, contribuie la definirea conceptului de optimizare a
unui program.
Compilatoarele pentru limbajele evoluate (C, C++, Pascal etc) conţin
facilităţi de generare de cod executabil optimizat în timp de execuţie, în dimensiune
a codului, sau combinaţii între acestea. Datorită specificaţiilor de limbaj, la nivel de
asamblare nu există asemenea facilităţi încorporate în asambloare, de aceea
aspectele de optimizare revin în exclusivitate programatorului.
În materialul de faţă s-a tratat problema optimizării timpului de execuţie,
care survine uneori în defavoarea dimensiunii codului. S-a considerat această
abordare deoarece la arhitecturile actuale, memoria nu mai este, în general, o
resursă critică. Resursa critică devine timpul, mai ales la aplicaţiile de prelucrare în
timp real, la prelucrări grafice, la jocuri etc.
661
mov ax, cx
se execută în 2 cicluri.
663
când operandul are adresa efectivă un număr impar, "forţarea" la baitul următor cu
adresa număr par presupune adăugarea a 4 cicluri. Instrucţiunea
mov SUMA, ax
..................
alfa: mov ax, 5
..................
jmp alfa
aduna PROC
push bp
mov bp,sp
mov ax,[bp+4]
add ax,[bp+6]
add ax,[bp+8]
pop bp
ret
aduna ENDP
ciclu:
add al,[bx]
adc ah,0
inc bx
loop ciclu
V k0 n1 k1 ... n p k p
unde:
k0 -numărul instrucţiunilor executate o singură dată;
ni -numărul de repetări ale buclei de program i;
ki -numărul de instrucţiuni cuprinse în bucla de program i.
Valoarea sa este V=11+4*n.
Există proceduri în care apar comparaţii şi se efectuează selecţii ale
secvenţelor. Se notează P j probabilitatea ca o condiţie Cj să fie îndeplinită, caz în
care se execută o secvenţă având volumul Vt. Numărul repetărilor testului pentru
condiţia Cj este nj.
Volumul operaţiilor este dat de relaţia:
V Pj Vf 1 Pj Vt n j .
Volumul de operaţii apare ca un număr mediu de operaţii care se vor executa în
timp. Procedura:
Tabelul 20.1.
Instrucţiunea Număr de repetări mediu
Mov al,[si] M
Cmp al,0 M
jz final M
Cmp al,’a’ M
jb urmat 0.033*M
Cmp al,’z’ 0.967*M
ja urmat 0.967*M
And al,NOT 20h 0.96*M
Mov [si],al 0.96*M
Inc si M
Jmp trans M
Ret 1
V = M*(6+0.967+0.967+0.96+0.96+0.033)+1
operaţii.
Conceptul de operaţie este general şi se observă de la început că
posibilitatea de a compara operaţiile este dificilă datorită diferenţei de complexitate
668
pe care fiecare operaţie o induce. Un transfer de date, intuitiv este mai simplu decât
o înmulţire, iar apelul unei proceduri este mai complex decât o implementare.
Se acceptă ipoteza conform căreia complexitatea operaţiilor este strâns
legată de numărul de cicluri maşină asociate. Diversităţii de instrucţiuni îi
corespunde o multitudine de numere de cicluri. Mai mult, tipurile de adresare
modifică numărul de cicluri pentru fiecare instrucţiune. Instrucţiunile care
manipulează un volum redus de informaţie sau au operanzi prefixati sunt puse în
corespondenţă cu un număr redus de cicluri. Instrucţiunile care au adrese ce sunt
calculate după formule complexe, care impun regăsiri, necesită un număr de cicluri
maşină superior.
Pentru a reflecta mai exact efortul de execuţie, volumul programului se va
exprima ca număr de cicluri maşină. Astfel, secvenţa:
.......................
mov ds,ax ; 2 cicluri masina
xor ax,ax ; 3 cicluri masina
inc ax ; 2 cicluri masina
cmp ax,20 ; 3 cicluri masina
...............................
rezultă necesitatea de a utiliza una din ultimele două variante, deşi prima
instrucţiune este mai sugestivă.
La tipul de adresare indexat este necesară incrementarea unui registru cu o
raţie egală cu lungimea zonei de memorie care este referită. Dacă raţia este o
unitate, din secvenţa:
rezultă că este avantajoasă utilizarea instrucţiunii inc si, efectul fiind major mai
ales pentru faptul că referirea este proprie unei secvenţe executate repetitiv.
Dacă raţia cu care se modifică registrul index este un număr oarecare,
repetarea instrucţiunii inc registru este ineficientă. Se optează spre una din
variantele din secvenţa:
De cele mai multe ori nu este posibilă alocarea unui registru pentru
memorarea raţiei şi se defineşte o constantă simbolică ( RATIA EQU 57) după care
incrementarea este realizată prin add si,RATIA.
Lucrurile devin mult mai simple dacă se pune problema alegerii modalităţii
de a înmulţi un număr cu 2k sau de a-l împărţi prin 2k. Pentru înmulţirea numărului
79 aflat în registrul AX cu 32 se alege secvenţa:
mov cl,5
mov ax,79
670
sal ax,cl ; 8+4*5 cicluri
întrucât secvenţa:
mov bl,32
mov al,79
cbw b ; 2 cicluri
mul bl ; 71 cicluri
mov bx,ax
mov al,operand_bait
cbw
add ax,bx
2
E = (a+b+c) * (a+b+c-d) + (c +a+b+c) / (a+b+c)
va fi scrisă de cele mai multe ori direct cum apare şi în rare cazuri se va calcula
E1=a+b+c după care se va calcula:
2
E = E1 * (E1-d) + (c +E1) / E1.
mov ax,a ;5
add ax,b ;7
add ax,c ;7
mov E1,ax ;3
sub ax,d ;7
mul E1 ;24
mov prod1,ax ;3
mov prod1+2,dx ;3
mov ax,c ;5
mul c ;24
add ax,E1 ;7
adc dx,0 ;7
div E1 ;25
xor dx,dx ;2
add ax,prod1 ;7
adc dx,prod1+2 ;7
mov E,ax ;3
mov E+2,dx ;3 total 149 cicluri (80286)
ţine seama de ceea ce conţin registrele după efectuarea operaţiilor şi utilizează acest
conţinut. Este puţin probabil ca programatorul să repete de patru ori secvenţa:
mov ax,a
add ax,b
add ax,c
673
mov cx,22
ciclu:
mov ax,0
mov si,0
add ax,x[si]
inc si
loop ciclu
mov total,ax
mov ax,0
mov si,0
mov cx,22
ciclu:
add al,x[si]
inc si
loop ciclu
V=10+29*37+24+10+29*37+14=2204
necesită un volum
V=13+37*47+5+28=1785
de cicluri (8088).
Dacă numărul de elemente ale unui şir este par se poate înjumătăţi numărul
de repetări prin calculul a două sume (suma elementelor cu poziţie pară şi suma
elementelor cu poziţie impară). La ieşirea din ciclu printr-o însumare se obţine
rezultatul dorit.
Secvenţa:
mov cx,N
xor ax,ax
xor bx,bx
xor si,si
ciclu: add ax,x[si]
add si,2
add bx,x[si]
add si,2
loop ciclu
add ax,bx
mov bx,0 ;1
xor ax,ax ;2
add ax,2 ;3
mov bx,ax ;4
.CODE
start:
jmp alfa
x dw 10
y dw 20
z dw ?
alfa:
mov ax,x
add ax,b
mov z,ax
mov ah,4ch
int 21h
END start
mov ax,5
cmp ax,0
jz alfa
..............
jmp beta
alfa:
.........
beta:
nop
este interpretată ca generatoare de cod mort dacă este inclusă chiar într-o structură
repetitivă, pentru că atât timp cât ax va conţine 5 şi se va compara cu valoarea zero,
secvenţa etichetată cu alfa nu se va executa. Volumul de operaţii nu este influenţat
dacă se ia în considerare coeficientul zero al probabilităţii acestei secvenţe inactive.
677
e=a+b+c
se construieşte secvenţa:
e=(a+b-c)*(a-b+c)
se calculează în secvenţa:
se obţin 52 cicluri.
Numărul de cicluri necesare pentru execuţia unei instrucţiuni cu operanzi
din memorie este mai mare decât în cazul în care operanzii s-ar afla în regiştrii.
Pentru reducerea timpului de execuţie, se va urmări păstrarea rezultatelor
intermediare în regiştrii liberi, printr-o alocare optimă a acestora.
Conform acestui principiu, secvenţa pentru calculul expresiei :
2
E=(a+b+c)*(a+b+c-d)+(c +a+b+c)/(a+b+c)
20.13 Concluzii
21
DESIGNUL LIMBAJELOR DE ASAMBLARE
N1
G= *100
2k
Γ = 100 – G
682
T T T T T T d w M M reg r/m
Biţii 7 6 5 4 3 2 1 0 7 6 5 4 3 2 1 0
d - direcţie de parcurgere;
w - tip operand (bait/cuvânt);
MM - interpretarea deplasării;
reg - registre (semiregistre) codificate;
r/m - tipologii expresii de adresare.
Limbajele de asamblare mai vechi, datorită numărului mare de registre şi a
aritmeticilor implementate, au regrupat expresiile de adresare pe un număr mai
restrâns de biţi. Gradul de dependenţă a câmpurilor din structura instrucţiunilor era
foarte slabă.
Limbajul de asamblare a microprocesorului convenţional x86 are definit un
grad de dependentă ridicat între elementele din structura instrucţiunii. Astfel,
deplasarea este interpretată funcţie de câmpul w (câmpul MM depinde de câmpul
w), iar câmpul reg depinde ca interpretare tot de câmpul w. Câmpul r/m depinde
de câmpul MM. Dacă valoarea MM este 11, câmpul r/m este interpretat reg.
21.5 Concluzii
Designul limbajului de programare cu luarea în considerare a finalităţii,
scrierea de programe, determină proiectarea unui limbaj pentru utilizator.
Numeroase dificultăţi care apar în asamblare, la generarea formei interne a
instrucţiunilor, sunt probleme independente de programator. Odată rezolvate
corect, programul asamblor va opera asupra unei mulţimi de programe în creştere,
propagând efectele pozitive ale limbajului.
Dacă la proiectare sunt luate în considerare şi elemente de compresie a
programelor în cod maşină, limbajul de asamblare oferă o trăsătură benefică tot la
nivelul utilizatorilor finali. Preocupările de design pentru limbaje de asamblare
capătă acum o nouă caracteristică, aceea de a fi deschis spre utilizatori. Opţiunea
spre neomogenitate de tratare a instrucţiunilor nu determină complicaţii la nivelul
utilizatorilor. La nivelul celor care implementează limbajul de asamblare, fiecare
neomogenitate se traduce în modalitate distinctă de tratare. Diversităţii de tipuri de
instrucţiuni îi va corespunde o creştere a complexităţii programului asamblor.
Pentru a obţine rezultate cu nivel de stabilitate ridicat este necesară crearea
unei baze de programe scrise în limbaj de asamblare care să includă cât mai multe
stiluri de programe şi cât mai multe tipuri de probleme rezolvate.
Ca şi în cazul altor limbaje, designul limbajelor de asamblare ia în
considerare menţinerea unui nivel ridicat al ortogonalităţii instrucţiunilor. Studiul
efectuat acum a presupus ortogonalitatea deja existentă a instrucţiunilor din
limbajul de asamblare asociat microprocesoarelor x86, fără a se proceda la
definirea de noi instrucţiuni sau noi tipuri de expresii de adresare. Dacă se vor
efectua în viitor şi aceste modificări, designul limbajului de asamblare capătă un
687
22
ELEMENTE DE GRAFICĂ
rezoluţie orizontală * rezoluţie verticală * memorie alocată pentru un pixel <= memoria video
modv db 0
.
.
; In segmentul de cod
.
.
mov ah, 0 ; setarea modului video
mov al, modv ; folosind intreruperea 10h
int 10h
.
.
MDA = 1;
HGC = 2;
CGA = 3;
EGA = 4;
VGA = 5;
.model SMALL
.code
Wich_adapter PROC
mov ax, 1B00h
int 10h
cmp al, 1Bh ; al va fi 1Bh pentru VGA
jne TestIfEGA
mov ax, VGA
jmp SHORT DoneAdapter
692
TestIfEGA :
mov ax, 40h
mov es, ax
test BYTE PTR es:[87h], 0FFh
jz TestIfCGAorHGC
mov ax, EGA
jmp SHORT DoneAdapter
TestIfCGAorHCG:
; mai intai obtine flag-ul echipamentului
int 11h
and al, 30h ; verifica modul video initial
cmp al, 30h
jne ItIsCGA
mov cx, 800h
mov dx, 3BAh
TestIfHGC:
in al, dx ; in HGC, bit-ul 7 al port-ului 3BAh
test al, 80h ; va fi 1 in timpul retragerii
verticale
jnz ItIsHGC ; ciclează pentru a vedea dacă bitul
devine 1
loop TestIfHGC
;altfel este MDA
mov ax, MDA
jmp SHORT DoneAdapter
ItIsHGC:
mov ax, HGC
jmp SHORT DoneAdapter
ItIsCGA:
mov ax, CGA
DoneAdapter:
ret
WichAdapter ENDP
END
.start
dataseg
VGA DB 0,1,2,3,4,5,20,7,56,57,58,59,60,61,62,63
end
rezoluţia orizontală este mai mică de 1024 se va pierde foarte multă memorie video
în acest mod.
Procedura de trasare a unei linii prezentată mai jos foloseşte, ca optimizare,
testarea faptului că ambele puncte extreme ale liniei sunt în aceeaşi secţiune. Dacă
ambele puncte sunt în aceeaşi secţiune, atunci nu este necesară schimbarea
secţiunii. Dacă ele nu sunt în aceeaşi secţiune, atunci pentru fiecare punct se
testează depăşirea secţiunii curente.
.486
code segment para public use16
assume cs:code
PgDown macro
push bx
push dx
xor bx,bx
mov dx,cs:winpos
add dx,cs:disp64k
mov cs:winpos,dx
call cs:winfunc
pop dx
pop bx
endm
PgUp macro
push bx
push dx
xor bx,bx
mov dx,cs:winpos
sub dx,1
mov cs:winpos,dx
call cs:winfunc
add di,cs:granmask
inc di
pop dx
pop bx
endm
mov ax,4f02h ;
mov bx,0101h ; modul VESA 101h (640x480, 256 culori)
int 10h ;
mov ax,0a000h
mov ds,ax
703
mov eax,10h
mov ebx,13h
mov ecx,20bh ; afişează linie
mov edx,1a1h
mov ebp,21h
call Lin
mov ax,4c00h
int 21h
GetVESA proc
; se iniţializează variabile în funcţie de granularitatea ferestrei
mov ax,4f01h
mov cx,0101h
lea di,buff ; folosim rutina VESA pentru
push cs ; a regăsi parametrii modului 101h
pop es
int 10h
add di,4
mov ax,word ptr es:[di] ;granularitatea ferestrei (în KB)
shl ax,0ah
dec ax
mov cs:granmask,ax ; = granularitatea - 1 (în Bytes)
not ax
clc
GVL1:
inc cs:bitshift
rcl ax,1
jc GVL1
add cs:bitshift,0fh
inc ax
mov disp64k,ax
add di,8
mov eax,dword ptr es:[di] ; adresa ferestri de control
mov cs:winfunc,eax
ret
buff label byte
db 100h dup (?)
endp
Lin proc
; Date de intrare: eax: x1, ebx: y1, cx: x2, dx: y2, bp: culoare
; Modifică: ax, bx, cx, edx, si, edi
; Foloseşte:
;
winfunc(dd),winpos(dw),page(dw),granmask(dw),disp64k(dw),bitshift(d
b)
; eax, ebx trebuie să aibă cuvintele semnificative setate pe 0
cmp dx,bx
ja LinS1 ; sortare vârfuri
704
xchg ax,cx
xchg bx,dx
LinS1:
sub cx,ax
ja LinS2 ; calcul delta_x
neg cx ; modifică bucla internă după semn
xor cs:xinc1[1],28h
LinS2:
sub dx,bx ;delta_y
neg dx
dec dx
shl bx,7
add ax,bx ; calcul adresă liniară de start
lea edi,[eax][ebx*4]
mov si,dx
xor bx,bx
mov ax,cs:page
shl ax,2 ; pageOffset=page*5*disp64K
add ax,cs:page
mul cs:disp64k
push cx ; iniţializează fereastră CPU
mov cl,cs:bitshift ; la punctul superior al liniei
shld edx,edi,cl
pop cx
add dx,ax
and di,cs:granmask
mov cs:winpos,dx
call cs:winfunc
mov dx,si
mov ax,bp
mov bx,dx
LinL1:
mov [di],al
add bx,cx
jns LinS3
LinE1:
add di,280h
jc LinR2 ; rutina de desenare
inc dx
jnz LinL1
jmp LinOut
LinL2:
mov [di],al
705
xinc1 label byte
LinS3:
add di,1 ; trecerea la următorul pixel pe
jc LinR1 ; orizontală
LinE2:
add bx,si
jns LinL2
jmp LinE1
LinR1:
js LinS7
PgDown ; mută pagina în jos cu 64k
mov ax,bp
jmp LinE2
LinS7:
PgUp ; sau în sus cu granularitatea
mov ax,bp
jmp LinE2
LinR2:
PgDown
mov ax,bp ; mută pagina în jos cu 64k
inc dx
jnz LinL1
LinOut:
mov cs:xinc1[1],0c7h
ret
endp
22.5 Concluzii
23
PROGRAME REZIDENTE
Externe
Hardware Nemascabile
Interne
Întreruperi BIOS
Sistem
DOS
Software
Utilizator
Figura 23.1 – Clasificarea întreruperilor
0000:0000h
offset rutină INT 0
segment rutină INT 0 0000:0004h
offset rutină INT 1
segment rutină INT 1 0000:0008h
offset rutină INT 2
segment rutină INT 2 0000:000Ch
0000:03FCh
offset rutină INT 255
segment rutină INT 255 0000:0400h
Organizarea memoriei
După instalarea sistemului de operare MS-DOS, memoria este structurată în
felul următor:
712
0000:0000h
Tabela vectorilor de
întrerupere
Zona de date BIOS 0040:0000h
Nucleul sistemului de
operare
Zona „System Data”
Memoria
convenţională Zona „System Code”
(640ko) Partea rezidentă a
COMMAND.COM
Zona proceselor
tranzitorii
A000:0000h
Memoria video
C000:0000h
Memoria BIOS video
superioară C800:0000h
BIOS periferice
(384ko) F000:0000h
ROM-BIOS
FFFF:000Fh
Figura 23.3 – Organizarea primului megaoctet de memorie internă sub sistemul de
operare MS-DOS
Tabelul 23.1.
Offset Lungime Semnificaţie
0 1 octet identificatorul blocului (M sau Z)
1 1 cuvânt adresa de segment a PSP al
proprietarului
3 1 cuvânt dimensiunea zonei de memorie
asociate (în paragrafe)
5 3 octeţi rezervat
8 8 octeţi numele proprietarului
adr_seg_MCBn+1=adr_seg_MCBn+dim+1
Tabelul 23.2.
Offset Lungime Semnificaţie
00h 1 cuvânt codul unei instrucţiuni INT 20h
02h 1 cuvânt adresa de segment a vârfului
memoriei de bază ocupate
04h 1 octet rezervat
05h 5 octeţi codul unei instrucţiuni CALL
seg_int21h:off_int21h
0Ah 2 cuvinte adresa rutinei de tratare INT 22h
0Eh 2 cuvinte adresa rutinei de tratare INT 23h
12h 2 cuvinte adresa rutinei de tratare INT 24h
16h 1 cuvânt adresa de segment a PSP al
procesului părinte
18h 20 octeţi rezervat
2Ch 1 cuvânt adresa de segment a blocului de
environment
2Eh 46 octeţi rezervat
5Ch 36 octeţi primul FCB standard, nedeschis
6Ch 20 octeţi al doilea FCB standard, nedeschis
80h 1 octet lungimea şirului cu parametrii
programului
81h 127 octeţi parametrii programului din linia de
comandă
pentru a executa un salt la locaţia de memorie imediat următoare PSP. Acesta este
întotdeauna punctul de intrare într-un program de tip COM.
Tabelul 23.3.
Offset Lungime Semnificaţie
00h 2 octeţi „MZ” (semnătura fişierelor .EXE)
02h 1 cuvânt PartPag = lungime fişier modulo
512
04h 1 cuvânt PageCnt = lungime fişier în pagini
de 512 octeţi
06h 1 cuvânt ReloCnt = număr de elemente din
tabela de relocare
08h 1 cuvânt HdrSize = dimensiune header în
paragrafe de 16 octeţi
0Ah 1 cuvânt MinMem = necesar minim de
memorie după sfârşitul programului
(în paragrafe)
0Ch 1 cuvânt MaxMem = necesar maxim de
memorie după sfârşitul programului
(în paragrafe)
0Eh 1 cuvânt ReloSS = deplasament segment
stivă
10h 1 cuvânt ExeSP = offset vârf stivă la
lansarea în execuţie
12h 1 cuvânt ChkSum = sumă de control
14h 1 cuvânt ExeIP = offset adresă de start
16h 1 cuvânt ReloCS = deplasament segment de
cod
18h 1 cuvânt TablOff = offset tabelă de relocare
1Ah 1 cuvânt indicator de overlay (0 pentru
modulele de bază)
TablOff 2*ReloCnt tabela de relocare
cuvinte
? ? octeţi caractere până la limita de paragraf
fie prin funcţia DOS 31h. Această funcţie menţine rezervată prima parte a zonei de
memorie ocupată de program, parte a cărei dimensiune este transmisă rutinei de
tratare a întreruperii apelate. Zona de memorie imediat următoare părţii marcate ca
rezidentă este eliberată şi apoi controlul este redat sistemului de operare sau
programului ce a lansat în execuţie programul rezident, dacă există un astfel de
program.
Orice program rezident este constituit din două mari părţi: o parte rezidentă
şi o parte nerezidentă. Partea nerezidentă trebuie să fie plasată în program după
partea rezidentă, deoarece funcţia TSR păstrează în memorie partea de început a
programului şi eliberează memoria ocupată de partea finală a programului.
0
P.S.P.
100h
jmp etichetă_nerezident
Rutine de tratare a
întreruperilor
int_x1: int_x2:
............
etichetă_nerezident:
Bloc decizie
instalare/dezinstalare/alte
operaţii
Date modul nerezident
Modul de instalare
pushf
cli
call dword ptr CS:vechea_rutina
Comutarea contextului
prompter. Ceea ce înseamnă că mai mereu această funcţie este activă. Pentru a
rezolva această problemă, sistemul de operare MS-DOS pune la dispoziţie
întreruperea INT 28h pe care sistemul o apelează de fiecare dată când se află în
aşteptare (de exemplu când interpretorul COMMAND aşteaptă introducerea unei
comenzi).
Această întrerupere indică faptul că programele rezidente se pot activa în
siguranţă. Rutina originală a întreruperii execută un simplu IRET. Programele
rezidente pot intercepta această întrerupere pentru a se activa la apelul ei. Pe
parcursul tratării acestei întreruperi pot fi apelate în siguranţă toate funcţiile DOS al
căror număr este mai mare decât 0Ch, cu menţiunea că funcţiile DOS 3Fh şi 40h nu
trebuie să folosească un handler ce referă dispozitivul CON.
int_8:
;codul rutinei de tratare int 8
int_10h:
;codul rutinei de tratare int 10h
int_13h:
;codul rutinei de tratare int 13h
int_88h:
;codul rutinei de tratare int 88h
nerezident:
;modulul nerezident
mov ah,02h
int 1Ah ;citesc ceasul de timp real
jc defect ;CF=1 -> ceas defect
det_cifre_BCD ch,0 ;determin cifrele orelor
det_cifre_BCD cl,6 ;determin cifrele minutelor
det_cifre_BCD dh,12 ;determin cifrele secundelor
rezident dacă acesta este instalat, respectiv din cadrul adresei de segment a
programului curent. Dacă şirurile sunt identice, atunci programul este deja instalat
şi se apelează modulul de dezinstalare, iar dacă şirurile diferă, atunci programul nu
a fost instalat şi se apelează modulul de instalare:
mov ax,cs
mov ds,ax
mov ax,3588h
int 21h ;ES:BX=adresa rutina INT 88h
mov di,offset semnatura
mov si,di ;DI si SI = offset semnatura
cld
mov cx,lung_sem
repe cmpsb ;compar sirurile DS:SI si ES:DI
jne instalare ;daca diferite, instalare
jmp dezinstalare ;daca identice, dezinstalare
mov ds,es:int8_veche_seg
mov dx,es:int8_veche_off
mov ax,2508h
int 21h ;restaurare int. 8
mov ds,es:int10h_veche_seg
mov dx,es:int10h_veche_off
mov ax,2510h
int 21h ;restaurare int. 10h
mov ds,es:int13h_veche_seg
mov dx,es:int13h_veche_off
mov ax,2513h
int 21h ;restaurare int. 13h
mov ax,0
mov ds,ax
mov dx,0
mov ax,2588h
int 21h ;dezactivare int. 88h
mov ah,49h
int 21h ;eliberare memorie TSR
23.9 Concluzii
24
741
subseturi ai acestora. Câţiva regiştri sunt accesaţi direct prin nume, ceilalţi fiind
setaţi şi citiţi prin instrucţiuni specifice. În continuare sunt reluate unele elemente
privind resursele sistemelor de calcul (regiştri, indicatori de stare) dar în contextul
lucrului protejat.
Regiştrii de segment
15 0
CS
DS
ES
SS
FS
GS
pot fi referiţi ca AH, AL, BH, BL, CH, CL, DH şi respectiv DL. Aceşti regiştri sunt
folosiţi pentru manipularea datelor în instrucţiuni.
31 15 7 0
16 8
EAX AH A X AL
EBX BH B X BL
ECX CH C X CL
EDX DH D X DL
ESI SI
EDI DI
EBP BP
ESP SP
EIP IP
31 17 16 15 7 0
Rezer- V R 0 N IO O D I T S Z 0 A 0 P 1 C
vat
M F T PL F F F F F F F F F
31 18 17 16 7 0
Rezer- A V R N IO O D I T S Z 0 A 0 P 1 C
vat
C M F T PL F F F F F F F F F
31 4 3 2 1 0
PG Rezervat ET TS EM MP PE
31 30 29 18 16 5 4 3 2 1 0
PG CD NW AM WP NE ET TS EM MP PE
31 16 15 0
I/O Map Base Reserved 64
Reserved LDT 60
Reserved GS 5C
Reserved FS 58
747
Reserved DS 54
Reserved SS 50
Reserved CS 4C
Reserved ES 48
EDI 44
ESI 40
EBP 3C
ESP 38
EBX 34
EDX 30
ECX 2C
EAX 28
EFLAGS 24
EIP 20
CR3 1C
Reserved SS2 18
ESP2 14
Reserved SS1 10
ESP1 0C
Reserved SS0 8
ESP0 4
Reserved Back link to previous TSS 0
Instrucţiuni utilizator
Instrucţiuni de sistem
15 3 2 1 0
TI RPL
TI – indicator de tabelă
RPL (Requested Privilege Level) – nivelul de prioritate solicitat
read_mode:
mov ax,data
mov ds,ax
lidt [oldidt] ;reincarcarea tabelei de
;intreruperi pentru modul real
sti ;reactivarea intreruperilor
Exemplu
Următorul exemplu demonstrează comutarea între mod real şi modul
protejat. Acest program pregăteşte structurile de date de bază pentru intrarea în
modul protejat. Programul nu include paginare, multitasking sau protecţie în modul
protejat.
În cadrul acestui program se folosesc două fişiere incluse:
STRUCT –defineşte majoritatea structurilor de date folosite în modul
protejat
MACRO1 –defineşte câteva macrodefiniţii folosite în programul
principal.
Fişierul STRUCT:
dscp struct
D_lim1 dw 0
D_base1 dw 0
D_base2 db 0
D_type db 0
D_lim2 db 0
D_base3 db 0
dscp ends
stkdef struct
oldeip dw 0
dw 0
13
oldcs dw 0
dw 0
oldflg dw 0
dw 0
oldsp dw 0
dw 0
oldss dw 0
dw 0
oldes dw 0
dw 0
oldds dw 0
dw 0
oldfs dw 0
dw 0
oldgs dw 0
dw 0
stkdef ends
page_tbl struc
pg_stat db ?
pg_avail db ?
pg_limit db ?
page_tbl ends
Fişierul MACRO1
TSS_cr3 macro
dd 0
endm
Program: EN.ASM
Tabela descriptorilor locali nu este folosită în acest program, astfel încât toţi
descriptorii sunt definiţi în tabela descriptorilor globali. În această tabelă se
defineşte doar dimensiunea şi tipul fiecărui descriptor, adresa de bază urmând a fi
setată la momentul execuţiei.
15
Această tabelă conţine acei selectori pentru care adresa de bază trebuie
iniţializată în descriptor. Segmentul corespunzător este definit folosind selectorul
asociat.
gdt_tab-size conţine numărul de intrări ale tabelei GDT.
Pasul 13: Setează regiştrii LDTR, SS, SP, DS, ES, FS şi GS.
Fişierul EN.ASM
.386p
include struct
include macro1
INTNO equ 21
DSCPSIZE equ 8
INTSIZE equ 4
TWO equ 2
prot_enable equ 01h
17
attribute equ 07h
space equ 20h
;Pasul 1: GDT
;Pasul 2: IDT
;Segmentul de date
Gdata_limit equ $
Gdata ends
19
;Pasul 6: Definirea segmentelor de stiva de nivel 0, 1 şi 2
;Pasul 7: TSS
;Segmentul de cod
mov ax,IDT
mov es,ax
mov di,offset idt_tab
20
mov ax,offset int_entry
mov cx,INTNO
fillidt:
mov es:[di],ax
add di,DSCPSIZE
axx ax,INTSIZE
loop fillidt
mov pGDT_addr,eax
mov pIDT_addr,eax
mov ax,GDT
mov es,ax
mov si,offset gdt_phys_tab
mov cx,gdt_tab_size
bdt1:
lodsw
mov bx,ax
and bx,0fff8h
lodsw
push ax
shl ax,4
mov es:[bx][d_base1],ax
pop ax
shr ax,12
mov es:[bx],d_base2],al
loop bdt1
cli
lgdt [pGDT]
lidt [pIDT]
mov eax,cr0
or al,prot_enable
mov cr0,eax
enter_prot:
dw offset now_in_prot
dw code_selec
now_in_prot:
xor ax,ax
lidt ax
mov ax,stk0_selec
mov ss,ax
mov sp,offset stk0_limit
mov ax,gdata_selec
mov ds,ax
mov es,ax
mov fs,ax
mov gs,ax
mov ax,video_selec
mov es,ax
mov cx,4000h
xor di,di
mov ah,attribute
mov al,space
rep stosw
mov si,offset in_protected
mov di,320
call disp_it
mov ax,task0_TSS_selec
ltr ax
22
;Pasul 16: Intoarcerea în modul real DOS
int 20
int_entry:
REPT INTNO
call disp
iret
ENDM
disp:
pop ax
mov bx,gdata_selec
mov ds,bx
sub ax,offset int_entry
shr ax,TWO
mov si,offset int_num
mov cx,TWO
call htoa
mov si,offset int_msg
mov di,5*160
call disp_it
cli
mov ax,dmy_selec
mov es,ax
mov ds,ax
mov fs,ax
mov gs,ax
mov ss,ax
mov eax,cr0
and eax,not prot_enable
mov cr0,eax
db 0eah
dw offset next_instruction
dw code
next_instruction:
mov ax,Gdata
mov ds,ax
mov ax,stk0
mov ss,ax
mov sp,offset stk0_limit
lidt [pold]
sti
mov ax,4c00h
23
int 21h
main endp
htoa_tab db ‘0123456789ABCDEF’
htoa proc near
xor ebx,ebx
add si,cx
dec si
htoal:
mov bl,al
and bl,0fh
mov bl,cs:[htoa_tab][ebx]
code ends
end main
24.5 Implementarea modului de lucru multitasking
segment TSS poate fi stocat doar în GDT. Dacă este invocată o comutare către un
proces al cărui descriptor este stocat în LDT, procesorul generează o excepţie.
Dacă a fost invocată o comutare prin intermediul unei instrucţiuni CALL sau
INT, întoarcerea către procesul iniţial se realizează printr-o instrucţiune IRET.
Exemplul care urmează realizează comutarea între două procese numite:
“task0” şi ”task1” folosind o instrucţiune JMP către descriptorul TSS al procesului
“task1”. După ce se realizează comutarea, procesul “task1” realizează întoarcerea
către procesul “task0” folosind o întrerupere software. Această întrerupere are
descriptorul definit în IDT fiind ceea ce se numeşte un descriptor de tip “task gate”
către descriptorul TSS al procesului “task0”.
Program: mult.asm
TSS0 dd 0 ;
dd 0,0 ;esp0,ss0
dd 0,0 ;esp1,ss1
dd 0,0 ;esp2,ss2
dd 0 ;cr3
dd 0 ;eip
dd 0 ;eflags
dd 0,0,0,0,0,0,0,0 ;eax,ecx,ebx,edx,esp,ebp,esi,edi
dd 0,0,0,0,0,0 ;es,cs,ss,ds,fs,gs
dd 0 ;LDT
TSS0_limit equ $
TSS1 dd 0
dd task1_esp0,task1_ss0 ;esp0,ss0
dd task1_esp1,task1_ss1 ;esp1,ss1
dd task1_esp2,task1_ss2 ;esp2,ss2
dd 0 ;cr3
dd task1_eip ;eip
dd task1_eflags ;eflags
dd task1_eax,task1_ecx,task1_ebx,task1_edx
;eax,ecx,ebx,edx
dd task1_esp,task1_ebp,task1_esi,task1_edi
;esp,ebp,esi,edi
dd task1_es,task1_cs,task1_ss,task1_ds ;es,cs,ss
dd task1_ds,task1_fs,task1_gs ;ds,fs,gs
dd 0
TSS1_limit equ $
dw task1_TSS_selec
dw task1_TSS
dw task1_code_selec
dw task1_seg
jmpf task1_TSS_selec
int21
task1_entry endp
task1_seg_limit equ $
task1_seg ends
24.6 Concluzii
25
PROGRAMAREA APLICAŢIILOR WINDOWS ÎN
LIMBAJ DE ASAMBLARE
FFFFFFFFH
Sistemul de operare Windows 9x
Drivere
C0000000H
4GB
BFFFFFFFH
80000000H
7FFFFFFFH
Programul Win32
2GB
00400000H
003FFFFFH
Compatibilitate cu Win16
00001000H
00000FFFH
00000000H
includelib \masm32\lib\user32.lib
.data
;
;datele iniţializate
;
.data?
;
;datele neiniţializate
;
.const
;
;constantele
;
.code
eticheta_de_inceput
;
;codul propriu zis
;
end eticheta_de_inceput
mov wcex.hCursor,eax
mov wcex.hbrBackground,COLOR_WINDOW+1
mov wcex.lpszMenuName,0
mov wcex.lpszClassName,offset NumeFer
mov wcex.hIconSm,0
.code
;...
;Inregistrarea clasei fereastra
push offset wcex
call RegisterClassEx
;...
push ID_MENIU
push hinst
call LoadMenu
mov hmeniu,eax
push 0 ; lpParam
push hinst ; hInstance
push hmeniu ; identificator meniu
push 0 ; identificator parinte
push CW_USEDEFAULT ; inaltime
push CW_USEDEFAULT ; latime
push CW_USEDEFAULT ; y
push CW_USEDEFAULT ; x
push WS_OVERLAPPEDWINDOW ; stilul
push offset NumeApp ; titlul ferestrei
push offset NumeFer ; numele clasei
push WS_EX_OVERLAPPEDWINDOW ; stilul extins
call CreateWindowEx
mov hwnd,eax
;...
Aşteaptă_mesaj:
Dacă este apare un mesaj, preia mesajul
Dacă mesajul este QUIT Atunci Ieşire
Apelează TranslateMessage
Apelează DispatchMessage
Trimite mesajul către procedura fereastră
Înapoi la Aşteaptă_mesaj
Ieşire
BOOL GetMessage(
LPMSG lpMsg, // adresa structurii cu mesajul
HWND hWnd, // identificatorul ferestrei
UINT wMsgFilterMin, // primul mesaj
UINT wMsgFilterMax // al doilea mesaj
);
40
start_bucla:
push 0 ; wMsgFilterMax
push 0 ; wMsgFilterMin
push NULL ; 0 - toate ferestrele
push offset msg ; lpMsg
call GetMessage ; returneaza FALSE pentru WM_QUIT
or eax,eax
41
jz iesire
Un mesaj ce trebuie tratat este WM_DESTROY care este trimis atunci când
utilizatorul doreşte să închidă fereastra. Mesajul este tratat standard, prin apelul
funcţiei PostQuitMessage, ce are prototipul:
VOID PostQuitMessage(
int nExitCode // codul de ieşire
);
.386
.model flat, stdcall
option casemap :none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
include res.equ
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
.data
.const
NumeFer db 'Programare sub Windows',0 ;Numele
ferestrei
NumeApp db 'Programare in Win32 ASM',0 ;Numele
aplicatiei
MesajDespre db 'Paul Pocatilu, 1999',0
Mesaj1 db 'Mouse click',0
.data?
wcex WNDCLASSEX <?> ;var de tip structura clasa fereastra
msg MSG <?> ;Var de tip MSG
hwnd dd ? ;identificatorul de fereastra
hinst dd ? ;identificatorul instantei curente
hmeniu dd ? ;identificatorul de meniu
.code
start:
;obtinerea hInstance
44
push 0
call GetModuleHandle
mov hinst,eax
mov wcex.hCursor,eax
mov wcex.hbrBackground,COLOR_WINDOW+1
mov wcex.lpszMenuName,0
mov wcex.lpszClassName,offset NumeFer
mov wcex.hIconSm,0
;Crearea ferestrei
push ID_MENIU
push hinst
call LoadMenu
mov hmeniu,eax
push 0 ; lpParam
push hinst ; hInstance
push hmeniu ; identificator meniu
push 0 ; identificator parinte
push CW_USEDEFAULT ; inaltime
push CW_USEDEFAULT ; latime
push CW_USEDEFAULT ; y
45
push CW_USEDEFAULT ; x
push WS_OVERLAPPEDWINDOW ; stilul
push offset NumeApp ; titlul ferestrei
push offset NumeFer ; numele clasei
push WS_EX_OVERLAPPEDWINDOW ; stilul extins
call CreateWindowEx
mov hwnd,eax
;Bucla de mesaje
start_bucla:
push 0 ; wMsgFilterMax
push 0 ; wMsgFilterMin
push NULL ; 0 - toate ferestrele
push offset msg ; lpMsg
call GetMessage ; returneaza FALSE pentru WM_QUIT
or eax,eax
jz iesire
iesire:
;Terminarea programului
push 0 ; codul de iesire returnat de aplicatie
call ExitProcess
;Functia Fereastra
WndProc proc
cmp dword ptr [esp+8],WM_COMMAND
je msg_wm_command
cmp dword ptr [esp+8],WM_DESTROY
je msg_wm_destroy
cmp dword ptr [esp+8],WM_LBUTTONDOWN
je msg_wm_lbuttondown
jmp DefWindowProc
msg_wm_command:
;se testeaza alegerea optiunilor din meniu
46
cmp dword ptr [esp+12],ID_FISIERE_IESIRE
je msg_wm_destroy
cmp dword ptr [esp+12],ID_HELP_DESPRE
je help_despre
xor eax,eax
ret 16
msg_wm_lbuttondown:
;s-a facut click in zona client a ferestrei
;se afiseaza o casuta de dialog
;avind ca titlu NumeApp si mesaj Mesaj1
push MB_OK
push offset NumeApp
push offset Mesaj1
push NULL
call MessageBox
xor eax,eax
ret 16
help_despre:
;s-a ales optiunea Despre... din meniul Help
;se afiseaza o casuta de dialog
;avind ca titlu NumeApp si mesaj MesajDespre
push MB_OK
push offset NumeApp
push offset MesajDespre
push NULL
call MessageBox
xor eax,eax
ret 16
msg_wm_destroy:
;s-a inchis fereastra
push 0
call PostQuitMessage
xor eax,eax
ret 16
WndProc endp
end start
ID_MENIU MENUEX
BEGIN
47
POPUP "&Fisiere", , , 0
BEGIN
MENUITEM "&Iesire", ID_FISIERE_IESIRE
END
POPUP "&Help", , , 0
BEGIN
MENUITEM "&Despre...", ID_HELP_DESPRE
END
END
BIBLIOGRAFIE