User Tools

Site Tools


laboratoare:laborator-04

Differences

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

Link to this comparison view

Both sides previous revision Previous revision
Next revision
Previous revision
laboratoare:laborator-04 [2015/11/03 18:15]
razvan.deaconescu [Breviar: Apelul unei funcții]
laboratoare:laborator-04 [2015/11/11 08:56]
razvan.deaconescu [Alte resurse]
Line 3: Line 3:
 În acest laborator vom prezenta modul în care se realizează apeluri de funcții. Vom vedea cum putem folosi instrucțiunile ''​call''​ și ''​ret''​ pentru a realiza apeluri de funcții și cum folosim stiva pentru a transmite parametrii unei funcții. În acest laborator vom prezenta modul în care se realizează apeluri de funcții. Vom vedea cum putem folosi instrucțiunile ''​call''​ și ''​ret''​ pentru a realiza apeluri de funcții și cum folosim stiva pentru a transmite parametrii unei funcții.
  
-Laboratorul este de forma //learn by doing// partea practică ​alterând ​între secțiuni de tip tutorial, cu parcurgere pas cu pas și prezentarea soluției, și exerciții care trebuie să fie rezolvate.+Laboratorul este de forma //learn by doing// partea practică ​alternând ​între secțiuni de tip tutorial, cu parcurgere pas cu pas și prezentarea soluției, și exerciții care trebuie să fie rezolvate.
  
 ===== Mediul de lucru ===== ===== Mediul de lucru =====
Line 24: Line 24:
 Descărcați arhiva, decomprimați-o și accesați directorul aferent. Descărcați arhiva, decomprimați-o și accesați directorul aferent.
  
-==== [1p] Recapitulare:​ Program în limbaj de asamblare ====+==== [1p] 1. Recapitulare:​ Program în limbaj de asamblare ====
  
 În SASM deschideți fișierul ''​NASMHello.asm'',​ fișier din instalarea implicită de NASM și compilați-l și rulați-l. Observați afișarea mesajului //Hello, world!// În SASM deschideți fișierul ''​NASMHello.asm'',​ fișier din instalarea implicită de NASM și compilați-l și rulați-l. Observați afișarea mesajului //Hello, world!//
Line 40: Line 40:
 Încheierea cu ''​\r\n''​ este, în general, utilă pentru afișarea șirurilor. Întrucât însă funcția ''​puts''​ pune automat o linie nouă după șirul afișat, prezența acestor caractere este opțională. Este, însă, utilă în cazul folosirii funcției ''​printf''​. Încheierea cu ''​\r\n''​ este, în general, utilă pentru afișarea șirurilor. Întrucât însă funcția ''​puts''​ pune automat o linie nouă după șirul afișat, prezența acestor caractere este opțională. Este, însă, utilă în cazul folosirii funcției ''​printf''​.
  
-==== [1p] Dezasamblarea unui program scris în C ====+==== [1p] 2. Dezasamblarea unui program scris în C ====
  
 După cum spuneam, în final, totul ajunge în limbaj de asamblare. Adesea ajungem să avem acces doar la codul obiect al unor programe și vrem să inspectăm modul în care arată. După cum spuneam, în final, totul ajunge în limbaj de asamblare. Adesea ajungem să avem acces doar la codul obiect al unor programe și vrem să inspectăm modul în care arată.
  
-Pentru a observa acest lucru, haideți să compilăm până la codul obiect un program scris în C și apoi să-l dezamblăm. Este vorba de programul ''​test.c''​ din arhiva de laborator.+Pentru a observa acest lucru, haideți să compilăm până la codul obiect un program scris în C și apoi să-l dezasamblăm. Este vorba de programul ''​test.c''​ din arhiva de laborator.
  
 Pentru a compila un program vom folosi linia de comandă și de acolo comanda ''​cl''​ care reprezintă compilatorul și linker-ul din Visual Studio. Pentru a compila un program vom folosi linia de comandă și de acolo comanda ''​cl''​ care reprezintă compilatorul și linker-ul din Visual Studio.
Line 60: Line 60:
 cl /c test.c cl /c test.c
 </​code>​ </​code>​
-În urma rulării comenzii de mai sus în directorul curent vom avea fișierul obiect test.c.+În urma rulării comenzii de mai sus în directorul curent vom avea fișierul obiect test.obj.
  
 Putem obține și forma în limbaj de asamblare a acestuia folosind comanda<​code>​ Putem obține și forma în limbaj de asamblare a acestuia folosind comanda<​code>​
Line 70: Line 70:
  
 Pentru a dezasambla codul unui modul obiect vom folosi un utilitar frecvent întâlnit în lumea Unix: ''​objdump''​. Pentru aceasta trebuie ca în prompt-ul Visual Studio sau în alt prompt să accesați directorul de binare al SASM. Este vorba de ''​C:​\Program Files (x86)\SASM\MinGW64\bin''​. De acolo, pentru dezasamblare,​ vom rula comanda<​code>​ Pentru a dezasambla codul unui modul obiect vom folosi un utilitar frecvent întâlnit în lumea Unix: ''​objdump''​. Pentru aceasta trebuie ca în prompt-ul Visual Studio sau în alt prompt să accesați directorul de binare al SASM. Este vorba de ''​C:​\Program Files (x86)\SASM\MinGW64\bin''​. De acolo, pentru dezasamblare,​ vom rula comanda<​code>​
-.\objdump.exe -d <​path-to-obj-file+.\objdump.exe -d <​path-to-obj-file>
 </​code>​ </​code>​
 unde ''<​path-to-obj-file>''​ este calea către fișierul obiect ''​test.obj''​. unde ''<​path-to-obj-file>''​ este calea către fișierul obiect ''​test.obj''​.
  
 Veți obține un output similar celui de mai jos<​code>​ Veți obține un output similar celui de mai jos<​code>​
-C:\Program Files (x86)\SASM\MinGW\bin>​.\objdump.exe -d C:​\Users\razvan\test.obj+C:\Program Files (x86)\SASM\MinGW\bin>​.\objdump.exe -d -M intel C:​\Users\razvan\test.obj
  
 C:​\Users\razvan\test.obj: ​    file format pe-i386 C:​\Users\razvan\test.obj: ​    file format pe-i386
Line 83: Line 83:
  
 00000000 <​_main>:​ 00000000 <​_main>:​
-   ​0: ​  ​55 ​                     push   %ebp +   ​0: ​  ​55 ​                     push   ebp 
-   ​1: ​  8b ec                   ​mov ​   ​%esp,%ebp +   ​1: ​  8b ec                   ​mov ​   ebp,esp 
-   ​3: ​  6a 0f                   ​push ​  $0xf+   ​3: ​  6a 0f                   ​push ​  0xf
    ​5: ​  e8 00 00 00 00          call   a <​_main+0xa>​    ​5: ​  e8 00 00 00 00          call   a <​_main+0xa>​
-   ​a: ​  83 c4 04                add    ​$0x4,%esp +   ​a: ​  83 c4 04                add    esp,0x4 
-   ​d: ​  ​50 ​                     push   %eax +   ​d: ​  ​50 ​                     push   eax 
-   ​e: ​  68 00 00 00 00          push   $0x0+   ​e: ​  68 00 00 00 00          push   0x0
   13:   e8 00 00 00 00          call   18 <​_main+0x18>​   13:   e8 00 00 00 00          call   18 <​_main+0x18>​
-  18:   83 c4 08                add    ​$0x8,%esp +  18:   83 c4 08                add    esp,0x8 
-  1b:   33 c0                   ​xor ​   ​%eax,%eax +  1b:   33 c0                   ​xor ​   eax,eax 
-  1d:   ​5d ​                     pop    ​%ebp+  1d:   ​5d ​                     pop    ebp
   1e:   ​c3 ​                     ret   1e:   ​c3 ​                     ret
   1f:   ​cc ​                     int3   1f:   ​cc ​                     int3
  
 00000020 <​_first_func>:​ 00000020 <​_first_func>:​
-  20:   ​55 ​                     push   %ebp +  20:   ​55 ​                     push   ebp 
-  21:   8b ec                   ​mov ​   ​%esp,%ebp +  21:   8b ec                   ​mov ​   ebp,esp 
-  23:   ​51 ​                     push   %ecx +  23:   ​51 ​                     push   ecx 
-  24:   c7 45 fc 03 00 00 00    ​movl   $0x3,-0x4(%ebp) +  24:   c7 45 fc 03 00 00 00    ​mov    DWORD PTR [ebp-0x4],0x3 
-  2b:   68 00 00 00 00          push   $0x0+  2b:   68 00 00 00 00          push   0x0
   30:   e8 00 00 00 00          call   35 <​_first_func+0x15>​   30:   e8 00 00 00 00          call   35 <​_first_func+0x15>​
-  35:   83 c4 04                add    ​$0x4,%esp +  35:   83 c4 04                add    esp,0x4 
-  38:   8b 45 fc                mov    -0x4(%ebp),%eax +  38:   8b 45 fc                mov    ​eax,DWORD PTR [ebp-0x4] 
-  3b:   ​50 ​                     push   %eax +  3b:   ​50 ​                     push   eax 
-  3c:   8d 4d 08                lea    ​0x8(%ebp),%ecx +  3c:   8d 4d 08                lea    ecx,[ebp+0x8] 
-  3f:   ​51 ​                     push   %ecx+  3f:   ​51 ​                     push   ecx
   40:   e8 00 00 00 00          call   45 <​_first_func+0x25>​   40:   e8 00 00 00 00          call   45 <​_first_func+0x25>​
-  45:   83 c4 08                add    ​$0x8,%esp +  45:   83 c4 08                add    esp,0x8 
-  48:   8b 45 08                mov    ​0x8(%ebp),%eax +  48:   8b 45 08                mov    eax,DWORD PTR [ebp+0x8] 
-  4b:   8b e5                   ​mov ​   ​%ebp,%esp +  4b:   8b e5                   ​mov ​   esp,ebp 
-  4d:   ​5d ​                     pop    ​%ebp+  4d:   ​5d ​                     pop    ebp
   4e:   ​c3 ​                     ret   4e:   ​c3 ​                     ret
   4f:   ​cc ​                     int3   4f:   ​cc ​                     int3
  
 00000050 <​_second_func>:​ 00000050 <​_second_func>:​
-  50:   ​55 ​                     push   %ebp +  50:   ​55 ​                     push   ebp 
-  51:   8b ec                   ​mov ​   ​%esp,%ebp +  51:   8b ec                   ​mov ​   ebp,esp 
-  53:   8b 45 08                mov    ​0x8(%ebp),%eax +  53:   8b 45 08                mov    eax,DWORD PTR [ebp+0x8] 
-  56:   8b 08                   ​mov ​   ​(%eax),%ecx +  56:   8b 08                   ​mov ​   ecx,DWORD PTR [eax] 
-  58:   03 4d 0c                add    0xc(%ebp),%ecx +  58:   03 4d 0c                add    ​ecx,DWORD PTR [ebp+0xc] 
-  5b:   8b 55 08                mov    ​0x8(%ebp),%edx +  5b:   8b 55 08                mov    edx,DWORD PTR [ebp+0x8] 
-  5e:   89 0a                   ​mov ​   ​%ecx,(%edx) +  5e:   89 0a                   ​mov ​   ​DWORD PTR [edx],ecx 
-  60:   ​5d ​                     pop    ​%ebp +  60:   ​5d ​                     pop    ebp 
-  61:   ​c3 ​                     ret+  61:   c3
 </​code>​ </​code>​
  
Line 160: Line 160:
 </​code>​ </​code>​
  
-În primă fază am plasat pe stivă argumentul funcției ''​puts''​ adică adresa șirului ''​msg''​. Apoi am apelat funcția ''​puts''​. Apoi am restaurat stiva (care crescuse prin apelul ''​push''​) ​scăzând ''​4''​ octeți (dimensiunea unui cuvânt pe 32 de biți) ​din registrul de stivă (''​esp''​).+În primă fază am plasat pe stivă argumentul funcției ''​puts''​ adică adresa șirului ''​msg''​. Apoi am apelat funcția ''​puts''​. Apoi am restaurat stiva (care crescuse prin apelul ''​push''​) ​adăugând ​''​4''​ octeți (dimensiunea unui cuvânt pe 32 de biți) ​la registrul de stivă (''​esp''​).
  
 Astfel sunt traduse majoritatea apelurilor de funcții. Dacă urmărim dezasamblarea fișierul ''​test.obj''​ putem observa acest șablon de apel și în alte părți. Mai jos sunt secvențele extrase din dezasamblarea de mai sus:<​code>​ Astfel sunt traduse majoritatea apelurilor de funcții. Dacă urmărim dezasamblarea fișierul ''​test.obj''​ putem observa acest șablon de apel și în alte părți. Mai jos sunt secvențele extrase din dezasamblarea de mai sus:<​code>​
Line 179: Line 179:
 </​code>​ </​code>​
  
-Contează mai puțin, pentru înțelegerea noastră din acest moment, de ce unele instrucțiuni arată cum arată, este importantă înțelegerea pașilor urmați pentru apelarea unei funcții. +Contează mai puțin, pentru înțelegerea noastră din acest moment, de ce unele instrucțiuni arată cum arată, este importantă înțelegerea pașilor urmați pentru apelarea unei funcții: plasarea argumentelor pe stivă, apelul funcției, restaurarea stivei
-==== [1p] Afișarea unui șir ====+==== [1p] 3. Afișarea unui șir ====
  
-(tutorial) Un program care afișează un string ​prin apelarea ​funcției print_string.+Pentru ​afișarea unui string ​în SASM putem folosi macro-ul ''​PRINT_STRING''​. Sau putem folosi o funcție precum ''​puts''​. În fișierul ''​print-string.asm''​ este implementată afișarea unui string folosind macro-ul ''​PRINT_STRING''​.
  
-(exercițiu) Un program care afișează lungimea unui string, ​folosind ​funcția print_num+Urmărind fișierul ''​hello-world.asm''​ ca exemplu, implementațafișarea șirului ​folosind ​și ''​puts''​.
  
-(exercițiu) Un program care afișează șirul inversat+<note tip> 
 +Urmărițși indicațiile din secțiunea de mai sus, legate de apelul unei funcții. 
 +</​note>​
  
-(tutorial) Un program care afișează un șir primit ca argument, parametru al funcției main+==== [2p] 4. Afișarea lungimii unui șir ====
  
-(exercițiu) Un program care face upper case la șirul primit ca argument (se presupune că este lowercase)+Programul ''​print-string-len.asm''​ afișează lungimea unui șir folosind macro-ul ''​PRINT_DEC''​. Calculul lungimii șirului ''​mystring''​ are loc în cadrul programului (este deja implementat).
  
-(exercițiu) Un program care face upper case doar dacă un caracter este lowercase.+Implementați programul pentru a face afișarea lungimii șirului folosind funcția ''​printf''​.
  
-(tutorial) Un program care implementează funcția ''​toupper'' ​care convertește un caracter la upper case dacă este lowercase.+La sfârșit veți avea afișată de două ori lungimea șirului: o dată cu apelul macro-ului SASM ''​PRINT_DEC''​ și apoi cu apelul ​funcției externe ​''​printf''​.
  
-(exercițiu) Un program care implementează funcția ​''​toupper_str'' ​care convertește un șir la upper case și care apelează în spate ''​toupper''​+<note tip> 
 +Gândiți-vă că apelul ​''​printf'' ​este de forma ''​%%printf("​String length is %u\n", len);%%''​. Trebuie să construiți stiva pentru acest apel.
  
-(tutorial) Apelare din libc a funcției ''​toupper''​+Pașii de urmat sunt: 
 +  - Marcarea simbolului ''​printf''​ ca simbol extern. 
 +  - Definirea șirului de formatare ''​%%"​String length is %u", 13, 10, 0%%''​. 
 +  - Realizarea apelului ​funcției ''​printf'',​ adică: 
 +    - Punerea celor două argumente pe stivă: șirul de formatarea și lungimea. 
 +    - Apelul ''​printf''​ folosind ''​call''​
 +    - Restaurarea stivei.
  
-(exercițiu) Un program care apelează ''​strlen''​ din libc și care afișează lungimea ​șirului primit ca argument ​în linia de comandă. Lungimea șirului este argumentul întors prin apelul ​''​strlen''​.+Lungimea ​șirului se găsește în registrul ​''​ecx''​. 
 +</​note>​
  
-**BONUS**: Implementarea ​unor funcții ''​rot13''​ și ''​rot13_str''​ care afișează varianta ​''​rot13''​ a unui șir primit ca argument ​în linia de comandă+==== [3p] 5. Afișarea șirului inversat ==== 
 + 
 +În soluția de mai sus adăugați funcția ''​reverse_string''​ astfel încât să aveți un listing similar celui de mai jos:<​code>​ 
 +[...] 
 +section .text 
 +global CMAIN 
 + 
 +reverse_string:​ 
 +    push ebp 
 +    mov ebp, esp 
 + 
 +    mov eax, dword [ebp+8] 
 +    mov ecx, dword [ebp+12] 
 +    add eax, ecx 
 +    dec eax 
 +    mov edx, dword [ebp+16] 
 + 
 +copy_one_byte:​ 
 +    mov bl, byte [eax] 
 +    mov byte [edx], bl 
 +    dec eax 
 +    inc edx 
 +    loopnz copy_one_byte 
 + 
 +    inc edx 
 +    mov byte [edx], 0 
 + 
 +    leave 
 +    ret 
 + 
 +CMAIN: 
 +    push ebp 
 +    mov ebp, esp 
 +[...] 
 +</​code>​ 
 + 
 +Funcția ''​reverse_string''​ inversează un șir și are următoarea signatură: ''​void reverse_string(const char *src, size_t len, char *dst);''​. Astfel ca primele ''​len''​ caractere și șirul ''​src''​ sunt inversate în șirul ''​dst''​. 
 + 
 +Realizați inversarea șirului ''​mystring''​ într-un nou șir și afișați acel nou șir. 
 + 
 +<note tip> 
 +Pentru a defini un nou șir, recomandăm ca, în secțiunea de date să folosiți construcția<​code>​ 
 +    store_string times 64 db 0 
 +</​code>​ 
 +Construcția creează un șir de 64 de octeți de zero, suficient pentru a stoca inversul șirului. 
 + 
 +Apelul echivalent în C al funcției este ''​reverse_string(mystring,​ ecx, store_string);''​. În registrul ''​ecx''​ am presupus că este calculată lungimea șirului. 
 + 
 +Nu puteți folosi direct valoarea ''​ecx''​ în forma ei curentă. După apelul funcției ''​printf''​ pentru afișare numărului valoarea ''​ecx''​ se pierde. Ca să o păstrați, aveți două opțiuni: 
 +  - Stocați valoarea registrului ''​ecx''​ în prealabil pe stivă (folosind ''​push ecx''​ înaintea apelului ''​printf''​) și apoi să o restaurați după apelul ''​printf''​ (folosind ''​pop ecx''​). 
 +  - Stocați valoarea registrului ''​ecx''​ într-o variabilă globală, pe care o definiți în secțiunea ''​.data''​. 
 + 
 +Nu puteți folosi un alt registru pentru că sunt șanse foarte mari ca și acel registru să fie modificat de apelul ''​printf''​ pentru afișarea lungimii șirului. 
 +</​note>​ 
 + 
 +==== Breviar: Apelatul în cadrul unei funcții ==== 
 + 
 +Atunci când apelăm o funcție spune că funcția care apelează (contextul care apelează) se cheamă ​**apelant** (sau **caller**) iar funcția apelată se cheamă **apelat** (sau **callee**). Până acum am discutat despre cum arată lucrurile la nivelul apelantului (cum construim stiva acolo). Haideți să urmărim ce se întâmplă la nivelul apelatului. 
 + 
 +Până în momentul instrucțiunii ''​call''​ stiva conține parametrii funcției. Apelul ''​call''​ poate fi echivalat grosier următoarei secvențe:<​code>​ 
 +    push eip 
 +    jmp function_name 
 +</​code>​ 
 + 
 +Adică și apelul ''​call''​ folosește în continuare stiva și salvează adresa următoarei instrucțiuni,​ cea de după ''​call''​ numită și instrucțiunea de retur sau adresa de retur (//return value//, //return address//). Aceasta este necesară pentru a ști, în apelat, unde să revenim. 
 + 
 +Suplimentar,​ în apelat, la începutul său (numit preambul, //​preamble//​) se salvează frame pointer-ul (în arhitectura i386 este vorba de registrul ''​ebp''​) urmând ca frame pointer-ul să refere adresa curentă de pe stivă (adică tocmai fostul frame pointer). Deși nu este obligatorie,​ salvarea frame pointer-ului ajută la debugging și este în cele mai multe cazuri folosită. Din aceste motive, orice apel de funcție va avea în general, preambulul:<​code>​ 
 +    push ebp 
 +    mov ebp, esp 
 +</​code>​ 
 + 
 +Aceste modificări au loc în apelat. De aceea este responsabilitatea apelatului să restaureze stiva la vechea sa valoare. De aceea este uzuală existența unui epilog care să readucă stiva la starea sa inițială; acest epilog este:<​code>​ 
 +    leave 
 +</​code>​ 
 + 
 +În acest moment stiva este ca la începutul funcției, adică imediat după ''​call'',​ referind adresa de retur. Urmează apelul<​code>​ 
 +    ret 
 +</​code>​ 
 +care este grosier echivalentul instrucțiunii:<​code>​ 
 +    pop eip 
 +</​code>​ 
 +Adică ia valoarea din vârful stivei și o plasează în ''​eip''​ urmând continuarea execuției programului de la acea adresă. 
 + 
 +Un sumar al acestui comportament,​ plasat pe apelul echivalent ''​reverse_string(mystring,​ len, store_string);''​ este indicat în diagrama de mai jos, accesibilă în format PDF și original DIA la adresa: http://​elf.cs.pub.ro/​asm/​res/​laboratoare/​lab-04-img/​ 
 + 
 +{{ :​laboratoare:​stack-in-function-call.png?​600 |}} 
 + 
 +De observat că pe parcursul execuției funcției, ceea ce nu se schimbă este poziția frame pointer-ul. Acesta este și motivul denumirii sale: pointează la frame-ul curent de funcției. De aceea este comun ca accesarea parametrilor unei funcții să se realizeze prin intermediul frame pointer-ului. Presupunând un sistem pe 32 de biți și parametri de dimensiunea cuvântului procesorului (32 de biți, 4 octeți) vom avea: 
 +  * primul argument se găsește la adresa ''​ebp+8''​ 
 +  * al doilea argument se găsește la adresa ''​ebp+12''​ 
 +  * al treilea argument se găsește la adresa ''​ebp+16''​ 
 +  * etc. 
 + 
 +Acesta este motivul pentru care, pentru a obține parametrii funcției ''​reverse_string''​ în registrele respectiv ''​eax'',​ ''​ecx'',​ ''​edx'',​ folosim construcțiile:<​code>​ 
 +    mov eax, dword [ebp+8] ​  ; retrieve first function argument in eax 
 +    mov ecx, dword [ebp+12] ​ ; retrieve second function argument in ecx 
 +    mov edx, dword [ebp+16] ​ ; retrieve third function argument in edx 
 +</​code>​ 
 +==== [2p] 6. Implementarea funcției toupper ==== 
 + 
 +Ne propunem implementarea funcției ''​toupper''​ care traduce literele mici în litere mari. Pentru aceasta, porniți de la fișierul ''​toupper.asm''​ din arhiva de exerciții a laboratorului și completați corpul funcției ​''​topupper''​. 
 + 
 +Șirul folosit este ''​mystring''​ și presupunem că este un șir valid. Acest șir este transmis ca argument funcției ​''​toupper'' ​în momentul apelului. 
 + 
 +Faceți înlocuirea //in place//, nu este nevoie de un alt șir. 
 + 
 +<note tip> 
 +Ca să traduceți o litera mică în literă mare, trebuie să scădeți ''​0x20''​ din valoare. Aceasta este diferența între litere mici și mari; de exemplu ''​a''​ este ''​0x61''​ iar ''​A''​ este ''​0x41''​. Puteți vedea în [[http://​man7.org/​linux/​man-pages/​man7/​ascii.7.html|pagina de manual ascii]]. 
 + 
 +Ca să citiți sau să scrieți octet cu octet folosiți construcția ''​byte [reg]''​ așa cum apare și în implementarea determinării lungimii unui șir  în fișierul ''​print-string-len.asm'',​ unde ''​[reg]''​ este registrul de tip pointer în care este stocată adresa ​șirului în acel punct. 
 + 
 +Vă opriți atunci când ați ajuns la valoarea ​''​0''​ (''​NUL''​ byte). Pentru verificare puteți folosi ''​test''​ așa cum se întâmplă și în implementarea determinării ​unui șir în fișierul ''​print-string-len.asm''​. 
 +</​note>​ 
 + 
 +==== [2p] Bonus: toupper doar pentru litere mici ==== 
 + 
 +Implementați funcția ''​toupper''​ astfel încât translatarea să aibă loc doar pentru caractare reprezentând litere mici, nu litere mari sau alte tipuri ​de caractere. 
 +==== [2p] Bonus: rot13 ==== 
 + 
 +Realizați și folosiți o funcție care face translatarea [[http://​www.decode.org/​|rot13]] a unui șir. 
 + 
 +==== [2p] Bonus: rot13++ ==== 
 + 
 +Implementați ''​rot13''​ pe un array de șiruri: șirurile sunt continue în memorie separate prin terminatorul de șirul (''​NUL''​-byte,​ ''​0''​). De exemplu: ''​ana\0are\0mere\0''​ este un array de trei șiruri. 
 + 
 +<note tip> 
 +Va trebui să știți când sa vă opriți din parcurgerea array-ului de șiruri. Cel mai simplu este să definiți o variabilă de lungime în secțiunea ''​.data'',​ de forma<​code>​ 
 +    len dd 10 
 +</​code>​ 
 +în care să rețineți fie lungimea totală a șirului (de la începutul până la ultimul ''​NUL''​-byte),​ fie numărul de șiruri din array. 
 +</​note>​ 
 +===== Soluții =====
  
-**BONUS**Un program care afișează varianta ''​rot13'' ​pentru ​toate șirurile primite ca argument în linia de comandă, și care afișează și un mesaj de eroare dacă nu este nici un șir transmis ca argument.+[[http://​elf.cs.pub.ro/​asm/​res/​laboratoare/​lab-04-sol.zip|Soluții de referință pentru ​exercițiile ​de laborator]]
  
 ===== Alte resurse ===== ===== Alte resurse =====
laboratoare/laborator-04.txt · Last modified: 2015/11/11 08:56 by razvan.deaconescu