Monday, January 20, 2014

ASM - Clase în limbaj de asamblare (notiuni introductive)

O clasă este o construcție prin care se definesc noi tipuri de date prin asocierea unui set de funcții(metode) la o structură de date. Iată un exemplu de clasă:

class Persoana{
private:
 char nume[25]; // vector de caractere unde vom salva numele persoanei
 int size; // variabila ce va memora dimensiunea numelui
public:
 Persoana(); // constructor implicit
 ~Persoana(); // destructor implicit
 void setNume(char*);
};
 
După cum am văzut mai sus apar niște etichete ce desemnează drepturi de acces asupra acelor elemente și anume: public, private și protected.
  • public: toate elementele de sub această etichetă pot fi accesate în interiorul clase cât și în afara acesteia.
  • private: elementele de sub această etichetă nu pot fi accesate decât în interiorul clasei.
  • protected: elementele din această categorie pot fi accesate doar în clasele ce derivă din această clasă.
Deoarece Assembler-ul nu ține cont de numele variabilelor acesta nu va ține cont nici de restricțiile impuse de aceste etichete. 
 
În principiu pentru a avea acces la un element din clasă trebuie ca cunoaștem practic adresa relativă fată de pointerul this la care se află elementul în memorie. Pentru aceasta avem nevoie să știm dimensiunea clasei. Dimensiunea clasei se calculează fiind suma dimensiunii fiecărui element.

În cazul de față clasa noastră ar avea dimensiunea de 29 de octeți. De fapt aceasta nu este dimensiunea reală deoarece se produce alinierea datelor, adica poziționarea lor în funcție de tipul cel mai mare de dată, în cazul nostru, int. Deci adevărata dimensiune a clasei este de 32 de octeți.

Cam așa arată clasa noastră în memorie.

Deoarece dimensiunea elementului nume este de 25, nefiind un multiplu de 4 (int fiind cel mai mare tip de dată din clasa), s-a produs alinierea datelor la tipul int.

Încă un lucru trebuie să mai știm ca să putem accesa un element din clasă, și anume că pointerul this se află în registrul ecx. Deci când lucrăm cu o clasă în limbaj de asamblare trebuie să avem grijă ca acest registru să nu fie modificat pentru a nu pierde adresa obiectului.

Adresa absolută a unui atribut din clasă se construiește din adresa de început a clasei, care se afla în pointerul this sau, în Assembler, în registrul ecx, și poziția de început atributul în memorie. În cazul nostru atributul size se găsește în memorie la adresa ecx+28, deoarece 28 reprezintă poziția de început a atributului size.

Acum ca avem aceste lucruri lămurite, putem trece să implementăm în limbaj de asamblare metoda din această clasă.

Ca idee de bază, verificăm dimensiunea șirului care este primit ca parametru, comparăm cu dimensiunea maximă a șirului din clasă. În cazul în care dimensiunea șirului primit ca parametru depășește dimensiunea șirului din clasă ieșim din funcție. Dacă dimensiunea este în regulă trecem mai departe la copierea celor șirurilor.

Pentru o bună vizualizare asupra codului, comentariile din partea de Assembler vor începe tot cu "//".

void setNume(char*){
 _asm{
  mov ebx,[ebp+8]
                   // mutăm în ebx valoarea parametrului primit
  xor edi, edi
                   // folosim registrul edi pentru a salva dimensiunea
                   // șirului primit ca argument
dim_param:
                   // calculăm dimensiunea parametrului primit ca argument
  cmp [ebx+edi],'\0'
  je verificare_dim
                   // cât timp nu am ajuns la sfârșitul șirului incrementăm registrul edi
  inc edi
  jmp dim_param
verificare_dim:
                   // verificam dacă depășește dimensiunea vectorului din clasă
  cmp edi,24
                   // comparăm cu dimensiunea maximă a șirului din clasă
  jge final

  xor esi, esi
bucla_1:
                   // copiem în vectorul din clasă șirul primit ca argument
                   // totodată ținând cont și de lungimea acestuia
  cmp [ebx+esi], '\0'
  je final_bucla_1
  mov dl, [ebx+esi]
                   // copiem fiecare caracter din șirul primit
                   // ca argument în șirul din clasa
  mov [ecx+esi], dl
  inc esi
  jmp bucla_1
final_bucla_1:
  mov byte ptr [ecx+esi],'\0'
  mov [ecx+28], esi
                   // punem dimensiunea actuală a șirului în variabila size
  jmp final
final:
 }
}

Mai sunt de explicat câteva lucruri în legătură cu codul de mai sus cum ar fi: folosirea registrului "dl " din instrucțiunea "mov dl, [ebx+esi]" sau de ce se pune "byte ptr" în instrucțiunea "mov byte ptr [ecx+esi],'\0'".

Pentru prima instrucțiune, folosirea registrului "dl" este necesară deoarece elementele din șir sunt de tip "char", dimensiunea lor fiind de 1 octet, avem nevoie de un registru de 1 octet pentru manipularea valorilor.

Cât despre a doua instrucțiune, practic îi spunem să pună "\0" într-un singur byte la adresa specificată.

No comments:

Post a Comment