O sa ma refer si eu un pic la PUSH si POP. 
Nu ca n-as fi de acord cu treaba cu farfuriile, da', dupa ce m-am jucat o leaca, a ajuns sa ma incurce. Pentru ca nu stiam esentialul.
Fiecarui fir de executie (thread) i se aloca la creare o zona de memorie numita stiva (stack) ce nu e in nici un fel diferita de alte zone de memorie. Un registru din procesor (sp, esp sau rsp, dupa cum lucram in 16, 32 sau 64 de biti) este setat sa contina capatul acestei adrese.
Exemplu:
sistemul de operare hotaraste sa aloce zona de la 0x400 la 0x500 pentru stiva. esp va contine valoarea 0x00000500.
Acum, exista cateva instructiuni care interactioneaza cu stiva.
1) PUSH - salveaza o valoare in stiva
Exemplu:
push eax ; salveaza valoarea registrului eax la esp-4, apoi scade din esp 4 => esp = 0x4FC
push 0 ; pune valoarea 0 la esp-4, apoi scade din esp 4 => esp = 0x4F8
push [0x123] ; salveaza valoarea de la adresa 0x123 la esp-4, apoi scade din esp 4 => esp = 0x4F4
2) POP - extrage o valoare din stiva in operator
Exemplu:
pop eax ; extrage valoarea care se gaseste la 0x123 si o plaseaza in eax; esp = esp +4 = 0x4F8
pop ecx ; extrage 0 si il plaseaza in ecx; esp = esp +4 = 0x4FC
pop [0x123] ; extrage valoarea anterioara a lui eax si pune in ecx; esp = esp+4 = 0x500
3) PUSHAD - salveaza toti registrii de uz general in stiva, in ordinea asta: EAX, ECX, EDX, EBX, ESP-ul original, EBP, ESI, si EDI
4) POPAD - restaureaza toti registrii de uz general
5) CALL - asa cum s-a mai spus, instructiunea asta transfera neconditionat executia la o alta procedura. In mod normal, dupa ce procedura e gata, executia se reia cu instructiunea urmatoare lui CALL

Inainte de a transfera executia, instructiunea salveaza valoarea registrului eip in stiva, iar esp = esp - 4
Exemplu:
; <- suntem aici
call O_Procedura ; <- executia va fi transferata la o alta procedura
; <- vom reveni aici
6) RET - este folosita - de obicei - in finalul unei proceduri, pentru a reveni la locul apelarii procedurii. Functioneaza astfel: valoarea din stiva indicata de esp este copiata in registrul EIP (cel care indica locul instructiunii curente), apoi la esp se adauga 4. RET are si un operand care ii spune procesorului cati BYTES trebuie sa scada din esp dupa ce a extras eip-ul. Operandul este folosit frecvent in conventia de apelare STDCALL (vezi mai jos), pentru a balansa stiva. Daca nu avem nici un parametru la procedura, operandul poate fi ignorat
Exemplu:
O_Procedura: ; Eticheta - in cazul nostru punctul de intrare in procedura
mov eax, ebx ; cateva instructiuni de umplutura
mov ecx, edx
; structura stivei:
; esp - adresa la care ne vom intoarce dupa executia procedurii si folosirea lui RET
; esp + 4 - parametrul furnizat procedurii
ret 4 ; in 32 de biti parametrul procedurii va fi de 4 bytes
; daca am avea 2 parametrii ar trebui sa scriem ret 8
.
.
.
push 1 ; furnizam parametrul procedurii in stiva
call O_Procedura ; apelam procedura
; <- dupa instructiunea ret executia revine aici
Conventii de apelareConventiile de apelare reglementeaza modul de transfer al parametrilor catre proceduri. Exista doua grupe mari si late: fie cel ce apeleaza e responsabil de balansarea stivei, fie cel ce e apelat.
1) STDCALLEste modelul prezent in exemplul meu de mai sus. Cel ce apeleaza pune argumentele in stiva si apeleaza procedura care, la final, va aduna la esp numarul argumentelor * 2 (in 16 biti) sau *4 (in 32 de biti).
; doua apeluri catre aceeasi procedura cu exact aceeasi parametrii
push eax
push ecx
call O_Alta_Procedura
sub esp, 8
mov [esp], ecx
mov [esp+4],eax
call O_Alta_Procedura
O_Alta_Procedura:
mov ecx, [esp + 4] ; la esp e plasat eip-ul, la esp+4 primul argument, la esp + 8 al doilea
mov eax, [esp + 8]
ret 8
2) CDECLTipul asta de apelare permite apelul catre o procedura care accepta numar variabil de parametrii (e, si la STDCALL putem sa facem asta daca ne gandim otzara). Apelantul plaseaza argumentele procedurii in stiva, apeleaza procedura, apoi balanseaza stiva.
push eax ; plasam argumentele
push ecx
call O_Procedura ; apelam procedura
call O_Procedura ; argumentele sunt inca in stiva, la esp si esp+4
add esp, 8 ; balansam stiva
O_Alta_Procedura:
mov ecx, [esp + 4] ; la esp e plasat eip-ul, la esp+4 primul argument, la esp + 8 al doilea
mov eax, [esp + 8]
ret ; nu ne jucam cu argumentele aici
3) Alte conventiiExista, desigur,
o gramada de alte conventii.
4) ConcluziiE important sa stii ce soi de conventie foloseste o functie pe care nu ai scris-o tu (de exemplu, functiile API din Windows) si sa folosesti conventia respectiva cand apelezi functia. Pentru functiile pe care le scrii tu, poti folosi exact ce-ti trece prin cap.
Elemente de limbaj inalt(asta e o traducere ca dracu' a "High level elements").
Majoritatea asambloarelor moderne (vezi FASM, MASM, NASM, SolASM, ...) iti ofera o modalitate comoda de a scrie proceduri si de a le apela.
PROC si ENDPFolosite in diferite moduri, te ajuta sa-ti organizezi procedurile. "In spatele scenei" proc va face - de fapt - ceea ce ai face si tu (sau nu). In primul rand, va defini o eticheta, care va deveni punctul de intrare in procedura.
O_Procedura PROC STDCALL uses edi esi Arg1:DWORD, Arg2:DWORD
xor eax, ecx
ret
O_Procedura ENDP
Sa le luam pe rand:
- asa cum ziceam, (macro)instructiunea va defini o eticheta cu numele O_Procedura (ca si cand ai scrie O_Procedura:);
- apoi poti sa setezi conventia de apelare (in cazul de fata STDCALL - vezi mai sus); sigur ca poti sa omiti in majoritatea cazurilor conventia, una fiind atribuita implicit.
-
uses te scapa de la a mai scrie tu push edi si push esi, precum si secventa inversa (pop esi pop edi)
- Arg1 - numele dat de tine si vizibil doar in interiorul procedurii (aaahhh, namespaces) pentru primul argument (cel mai aproape de call, cand apelezi procedura)
- Arg2 - ...

De cele mai multe ori, se va mai introduce o secventa de code, si anume:
push ebp
mov ebp, esp
Si asa apare in scena Extended Base register. Asta e un registru ca oricare altul (general purpose), dar e des folosit pentru a simplifica lucrul cu stiva. Se poate si fara el (pe cuvant).
Ce se intampla aici? EBP este salvat in stiva, apoi el ia valoarea lui esp. Astfel, pe toata durata executiei procedurii (hopefully ... ) vom avea o copie a pointerului in stiva de la inceputul executiei procedurii. Si cum stim ca,la momentul respectiv, la esp e adresa de intoarcere, la esp + 4 e primul argument, la esp + 8 al doilea, ... putem sa accesam exact la fel argumetele si cu ebp (+4, +8). Mai important, atunci cand executi o instructiune care modifica esp-ul (un push, un call, ...), nu vei mai accesa argumentul 1 la esp + 4, ci la esp + 8 (na, un exemplu). Dar il vei accesa intotdeauna la ebp + 4. In codul tau, vei folosi numele simbolic, desigur.
mov eax, Arg1
sau
mov eax, [Arg1], dupa ce asamblor folosesti.
dar ambele instante vor fi inlocuite cu
mov eax, [ebp+4]
Totusi, mi se pare important sa stii ce se intampla "in spatele scenei".
O alta intrebuintare a lui ebp e la folosirea variabilelor locale in proceduri. In masm definesti o variabila locala folosind LOCAL:
LOCAL LocVar1:DWORD
LOCAL Rect1:RECT
ce se va intampla? dupa secventa push ebp - mov ..., va mai fi introdusa si instructiunea:
sub esp, 20 ;(4 pt dword, 4 * 4 pentru RECT)
creind in stiva un spatiu de 20 de bytes. Si da, ai ghicit, atunci cand folosesti numele simbolice in cod:
mov eax, LocVar1
mov Rect1.right, 12
ele vor fi inlocuite cu:
mov eax, [ebp-4]
mov [ebp-16],12 ; sper c-am calculat-o bine pe asta ;)
Recapituland, linia ta:
O_Procedura PROC STDCALL uses edi esi Arg1:DWORD, Arg2:DWORD
va fi inlocuita de secventa:
O_Procedura:
push edi ; de la uses
push esi
push ebp ; ii zice stack frame
mov ebp, esp
; iar daca inseram si variabilele locale de mai sus
sub esp, 20
Nu spun ca - de fiecare data - se va proceda exact in felul asta dar, functional, schema va fi similara.
Acu', trebuie sa existe si o balansare a stivei undeva. In MASM instructiunea ret nu e tocmai instructiunea ret ...

De fiecare data cand o folosesti, ea va fi inlocuita de (folosind in continuare exemplul anterior):
mov esp, ebp ; chiar daca ai debalansat stiva, asta te salveaza
pop ebp
pop esi
pop edi
ret 8 ; (2 argumente * 4 bytes)
... Continuarea maine...

Nicu