Differences

This shows you the differences between two versions of the page.

Link to this comparison view

iocla:laboratoare:laborator-06 [2019/11/02 14:38]
laurentiu.stefan97
— (current)
Line 1: Line 1:
-====== Laborator 06: Lucrul cu stiva ====== 
- 
-În acest laborator vom învăța cum este reprezentată stiva in limbajul de asamblare, care este utilitatea ei si cum se programeaza cu ajutorul acesteia. 
- 
-===== Terminologie ===== 
- 
-In lumea algoritmicii,​ stiva este o structura de date abstracta prin intermediul careia se poate reprezenta informatie ce respecta regula "​primul venit, ultimul servit"​. 
- 
-In lumea programarii assembly, stiva este o zona rezervata de memorie folosita pentru a tine evidenta operatiilor interne ale unui program: functii, adrese de retur, parametrii pasati, etc. Astfel, stiva poate fi vazuta ca o implementare la nivel hardware a stivei abstracte. 
- 
-===== Utilitatea stivei ===== 
- 
-    * **Apeluri de functii**. Fiecare apel de functie necesita, printre altele, memorarea adresei de retur. Imaginati-va ca aveti un numar de functii ​ N ce se apeleaza succesiv : prima functie o apeleaza pe a 2-a, a 2-a pe a 3-a si asa mai departe; cand functia N a fost apelata si si-a terminat executia, programul trebuie sa continue in contextul in care a avut loc apelul. ​ Daca retinem adresele de retur intr-o stiva, ordinea apelurilor se pastreaza in mod natural. 
-    * **Variabile temporare**. In general, programele folosesc un numar mare de variabile si rezultate partiale; avand in vedere ca exista un numar limitat de registre, este posibil ca la un moment dat, sa nu avem niciun registru disponibil pentru a retine valoarea unei operatii. In cazul acesta, putem memora rezultatul la o adresa de memorie pe care o definim in zona de date. Aceasta metoda are dezavantajul ca vom "​polua"​ tabela de offset-uri cu variabile pe care le folosim o singura data. In loc sa facem aceasta pentru fiecare rezultat partial, este mai usor sa punem pe stiva valoarea unui registru pentru a il putea folosi si a o recupera in momentul in care nu mai folosim variabila temporara. ​ 
- 
-===== Operatii asupra stivei ===== 
- 
-Stiva poate fi modificata in 2 moduri: 
- 
-1. Prin utilizarea instructiunilor special implementate pentru lucrul cu stiva, dintre care cele mai uzuale sunt ''​push''​ si ''​pop'':​ 
- 
-<code asm> 
- 
-%include "​io.inc"​ 
-  
-section .text 
-global CMAIN 
-CMAIN: ​                       ​ 
- 
-    mov eax, 7 
-    mov ebx, 8 
-    add eax, ebx 
-    push eax            ; pune pe stiva continutul registrului eax 
-    mov eax, 10         ; acum putem folosi registrul, intrucat valoarea lui este salvata pe stiva 
-    PRINT_UDEC 4, eax   ; 10 
-    NEWLINE 
-    ​ 
-    pop eax             ; recupereaza valoarea registrului eax 
-    PRINT_UDEC 4, eax   ; 15 
- 
-</​code>​ 
- 
-2. Adresand memoria, cu ajutorului registrului in care este tinut pointerul catre capul stivei ("​stack pointer"​) ''​esp'':​ 
- 
-<code asm> 
- 
-%include "​io.inc"​ 
-  
-section .text 
-global CMAIN 
-CMAIN: ​                       ​ 
-    mov eax, 7 
-    mov ebx, 8 
-    add eax, ebx 
-    sub esp, 4           ; rezerva 4 octeti pe stiva 
-    mov [esp], eax       ; muta la noua adresa catre care pointeaza esp continutul registrului eax 
-    mov eax, 10 
-    PRINT_UDEC 4, eax 
-    NEWLINE 
-    ​ 
-    mov eax, [esp]       ; recupereaza valoarea de pe stiva 
-    add esp, 4           ; restabileste valoarea registrului esp 
-    PRINT_UDEC 4, eax 
- 
-</​code>​ 
- 
-<note important>​ Comentati instructiunile ''​sub esp, 4''​ si ''​add esp, 4''​. Ce se intampla? De ce?</​note>​ 
-<note tip> Stiva este folosita pentru a memora adresa de retur in momentul in care o functie este apelata</​note>​ 
- 
-**Remarcati faptul ca stiva creste de la adrese mari la adrese mici**. Acesta este motivul pentru care alocarea memoriei pe stiva se face folosind instructiunea ''​sub'',​ iar eliberarea se face folosind instructiunea ''​add''​. 
- 
-{{ :​iocla:​laboratoare:​stack.png?​300 |}} 
- 
-Unele procesoare nu au suport pentru lucrul cu stiva: spre exemplu, procesoarele [[https://​en.wikipedia.org/​wiki/​MIPS_architecture|MIPS]] nu au instructiuni ''​push''​ si ''​pop''​ si nici un registru special pentru stack pointer. Astfel, daca am dori sa implementam operatiile pe stiva in procesorul MIPS aceasta s-ar realiza exact ca in exemplul de mai sus, doar ca am putea sa ne alegem noi orice registru pentru a tine minte stack pointerul. 
- 
-Asadar, instructiunea ''​push eax'',​ pe un procesor x86, este echivalenta cu: 
-<code asm> 
-sub esp, 4 
-mov [esp], eax 
-</​code>​ 
- 
-Iar instructiunea ''​pop eax'':​ 
-<code asm> 
-mov eax, [esp] 
-add esp, 4 
-</​code>​ 
-===== Tricks and tips ===== 
- 
-1. Regula de aur a utilizarii stivei este : numarul de ''​push''​-uri == numarul de ''​pop''​-uri intr-o functie. Avand in vedere ca stiva este folosita pentru apelarea functiilor, este foarte important ca in momentul in care o functie isi termina executia, sa actualizeze ''​esp''​-ul astfel incat acesta sa indice catre aceeasi zona de memorie (a stivei) catre care indica in momentul intrarii in functie. 
- 
-2. Avand in vedere ca exista situatii in care facem un numar N de ''​push''​-uri si ajungem la finalul functiei fara sa fi facut ''​pop''​ pentru niciuna dintre valori, putem restabili capul stivei folosind instructiunea ''​add''​. 
- 
-<code asm> 
- 
-%include "​io.inc"​ 
-  
-section .text 
-global CMAIN 
-CMAIN: 
-    mov eax, 5 
-    mov ebx, 6 
-    mov ecx, 7 
-    ​ 
-    push eax 
-    push ebx 
-    push ecx 
-    ​ 
-    add esp, 12     ; echivalent cu utilizarea a 3 pop-uri consecutive 
-    ret 
- 
-</​code>​ 
- 
-3. Metoda de mai sus are dezavantajul ca tot trebuie sa cautam prin program cate ''​push''​-uri am facut (ceea ce poate sa necesite destul de mult timp in programele din viata reala). Daca nu vrem sa nu ne batem deloc capul cu stack pointerul, 
-putem sa folosim urmatoarea constructie:​ 
- 
-<code asm> 
- 
-%include "​io.inc"​ 
-  
-section .text 
-global CMAIN 
-CMAIN: 
-    ​ 
-    mov ebp, esp       ; salveaza stack pointerul curent 
-    ​ 
-    mov eax, 5 
-    mov ebx, 6 
-    mov ecx, 7 
- 
-    push eax 
-    push ebx 
-    push ecx 
-    ​ 
-    mov esp, ebp       ; restaureaza stack pointerul 
-    ret 
- 
-</​code>​ 
- 
-<note important>​ Care este intrebuintarea principala a registrului ''​ebp''?​ </​note>​ 
- 
-===== Exerciții ===== 
- 
-În cadrul exercițiilor vom folosi [[http://​elf.cs.pub.ro/​asm/​res/​laboratoare/​lab-06-tasks.zip|arhiva de laborator]]. 
- 
-Descărcați arhiva, decomprimați-o și accesați directorul aferent. 
- 
-==== [1p] 0. Recapitulare:​ Media aritmetică a elementelor dintr-un vector ==== 
- 
-Pornind de la exercițiul ''​0-recap-mean.asm''​ din arhiva de laborator, implementați codul lipsă, marcat de comentarii de tip ''​TODO'',​ pentru a realiza un program care calculează media aritmetică a elementelor dintr-un vector. Afișați doar partea întreagă a mediei (câtul împărțirii). 
- 
-<note tip> 
-Dacă ați făcut calculul corect, suma elementelor vectorului va fi ''​3735''​ iar media aritmetică a elementelor din vector va fi ''​287''​. 
-</​note>​ 
- 
-==== [1p] 1. Max ==== 
- 
-Calculați maximul dintre numerele din 2 registre (''​eax''​ și ''​ebx''​) folosind o instrucțiune de comparație,​ o instrucțiune de salt și instrucțiuni ''​push''/''​pop''​. 
- 
-<note tip> 
-Gandiți-vă cum puteți să interschimbați două registre folosind stiva. 
-</​note>​ 
-==== [1.5p] 2. Construirea array-ului inversat ​ ==== 
- 
-Pornind de la exercițiul ''​reverse-array.asm'',​ implementați TODO-urile **fără a folosi instrucțiunea ''​mov''​ în lucrul cu array-urile** astfel încât în array-ul ''​output''​ la finalul programului să se afle array-ul ''​input''​ inversat. ​ 
- 
-<​note>​ 
-Dupa o rezolvare corecta programul ar trebui sa printeze: 
-<​code>​ 
-Reversed array: 
-911 
-845 
-263 
-242 
-199 
-184 
-122 
-</​code>​ 
-</​note>​ 
- 
-==== [5p] 3. Adresarea si printarea stivei ​ ==== 
- 
-Programul ''​stack-addressing.asm''​ din arhiva laboratorului alocă și inițializează două variabile locale pe stivă: 
-  * un array format din numerele naturale de la 1 la ''​NUM''​ 
-  * un string "Ana are mere". 
- 
-  - [**1p**] Înlocuiți fiecare instrucțiune ''​push''​ cu o secvență de instrucțiuni echivalentă. 
-  - [**2p**] Printați adresele și valorile de pe stivă din intervalul **[ESP, EBP]** (de la adrese mici la adrese mari) octet cu octet. 
-  - [**1p**] Printați string-ul alocat pe stivă octet cu octet și explicați cum arată acesta în memorie. Gândiți-vă de la ce adresă ar trebui să afișați și când ar trebui să vă opriți. 
-  - [**1p**] Printați vectorul alocat pe stivă element cu element. Gândiți-vă de la ce adresă ar trebui să începeți afișarea și ce dimensiune are un element. 
- 
-<note tip>​Citiți sectiunea [[https://​ocw.cs.pub.ro/​courses/​iocla/​laboratoare/​laborator-07#​operatii_asupra_stivei|Operatii asupra stivei]].</​note>​ 
- 
-<​note>​ 
-După o implementare cu succes, programul ar trebui să afișeze ceva de genul (nu fix același lucru, adresele de pe stivă putând să difere): 
-<​code>​ 
-0xffcf071b: 65 
-0xffcf071c: 110 
-0xffcf071d: 97 
-0xffcf071e: 32 
-0xffcf071f: 97 
-... 
-0xffcf0734: 4 
-0xffcf0735: 0 
-0xffcf0736: 0 
-0xffcf0737: 0 
-0xffcf0738: 5 
-0xffcf0739: 0 
-0xffcf073a: 0 
-0xffcf073b: 0 
-Ana are mere 
-1 2 3 4 5 
-</​code>​ 
-Explicați semnificația fiecărui octet. De ce sunt puși în ordinea respectivă?​ De ce unii octeti sunt 0?  
-</​note>​ 
- 
-<note tip> 
-Amintiți-vă ce valoare au caracterele în reprezentarea zecimală(codul ASCII).\\ 
-Amintiți-vă în ce ordine sunt ținuți octeții unui număr: revedeți secțiunea ''​Ordinea de reprezentare a numerelor mai mari de un octet''​ din [[https://​ocw.cs.pub.ro/​courses/​iocla/​laboratoare/​laborator-01|Laboratorul 01]]. 
-</​note>​ 
- 
-==== [1.5p] 4. Variabile locale ​ ==== 
- 
-Programul ''​merge-arrays.asm''​ din cadrul arhivei de laborator, îmbină două array-uri sortate crescător (''​array_1''​ și ''​array_2''​) punând array-ul rezultat în ''​array_output''​ definit în secțiunea ''​.data''​. 
- 
-Modificați programul astfel încat ''​array_output''​ să fie alocat pe stivă. 
- 
-/* 
- 
-<note tip>  ​ 
-  - Alocati spatiu pe stiva pentru array-uri si initializati-le 
-  - Aflati offset-ul fiecarui array fata de EBP (EBP - x) 
-  - Inlocuiti simbolurile array_1, array_2 etc cu adresele aflate 
-</​note>​ 
- 
-*/ 
-==== [2p] 5. BONUS: GCD - Greatest Common Divisor ​ ==== 
- 
-Deschideți ''​gcd.asm''​ și rulați programul. Codul calculează cel mai mare divizor comun dintre două numere date ca și parametru prin registrele eax și edx, și pune valoarea aflată tot în registrul eax. 
-  - [**1p**] Faceți modificările necesare astfel încat mesajul de eroare - ''​The program crashed!''​ - să nu mai apară. 
-  - [**1p**] În cadrul label-ului ''​print''​ afișați rezultatul sub forma: 
- 
-<​code>​ 
-gdc(49,​28)=7 
-</​code>​ 
  
iocla/laboratoare/laborator-06.1572698316.txt.gz · Last modified: 2019/11/02 14:38 by laurentiu.stefan97
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0