Sunteți pe pagina 1din 13

Programarea in limbaj de asamblare - introducere

Sursa: Radu Lupsa, UBB Cluj-Napoca (Documentatie veche, 2001) http://www.cs.ubbcluj.ro/~rlupsa/edu/ac/doc/ Mai intai, cum scriem un program in limbaj de asamblare? Programul se va scrie intr-un fisier text cu extensia asm. Un exemplu minimal:
arit1.asm DATA segment a db b db c db s_ab db s_abc db _DATA ends

20 16 30h 0 0

; ; ; ; ;

20 = 16 = 48 = aici aici

14h 10h 30h vom pune a+b vom pune a+b+c

_TEXT segment assume cs:_TEXT assume ds:_DATA start: ; de aici incepe executia programului mov ax, _DATA ; incarcam in ds adresa de segment a segmentului mov ds, ax ; de date. Nu putem face direct (mov ds, _DATA ; nu are operanzi compatibili (vom vedea mai ; tarziu de ce. mov add mov add mov al, a al, b s_ab, al al, c s_abc, al ; de aici incepe programul propriu-zis ; al=s_ab=a+b ; al=s_abc=a+b+c ; terminam executia programului mov ax, 4C00h int 21h _TEXT ends _STACK segment stack db 500 dup(?) _STACK ends end start ; aici marcam punctul d intrare in progrm

============== Pentru "compilare" veti da comenzile


tasm /zi

nume

urmata de
tlink /v

nume

Important! 1. Numele programului se va da fara extensie. 2. Executabilele tasm si tlink se gasesc in acelasi loc cu mediul Turbo Pascal sau Borland C++ Rezultatul celor doua comenzi de mai sus este un program executabil (.exe). Depanarea programelor scrise in asamblare (si pentru inceput, chiar urmarirea executiei lor) se face cu ajutorul programului Turbo Debugger. Comanda este:
td

nume

Urmarirea programului cu Turbo Debugger se va face in fereastra CPU (se deschide din meniul View|CPU). Aici avem

continutul registrilor si flagurilor procesorului programul ce se executa. Acesta este prezentat atat in limbaj de asamblare cat si direct in cod masina (adica, octet cu octet, continutul memoriei) continutul segmentului de date (in hexa) continutul stivei

Putem pozitiona zona vizibila din program, zona de date si stiva folosind comanda Go to din meniul local al ferestrei (meniul activat cu Alt-F10). De asemenea, putem afla sau modifica valoarea unei variabile, folosind fereastra de dialog Evaluate/Modify (deschisa cu Ctrl-F4)

Exercitii
1. Generati program executabil din sursa prezentata in aceasta pagina 2. Urmariti pas cu pas executia programului (in Turbo Debugger). Urmariti continutul registrelor ax si ip in acest timp.

3. Tot in executia pas cu pas a programului, urmariti continutul segmentului de date. Pentru aceasta, activati (cu Shift-sageata jos) zona de date a ferestrei CPU, activatati meniul local (Alt-F10) si dati comanda Go to. In fereastra ce se deschide, dati ds:0. Urmariti primii octeti din segmentul de date pe parcursul executiei programului. 4. Pozitionati afisarea zonei de date pe cs:0 Comparati cu ceea ce se afiseaza in zona de cod. 5. Modificati programul in asa fel incat variabilele sa fie reprezentate pe 16 biti si initializati-le cu valori mai mari (de ordinul 1000). Repetati exercitiile 1-4. 6. Acelasi lucru acum (exercitiile 1-4) pentru programul arit2.asm. Ce greseala observati? Corectati-o.
_DATA segment a dd b dd c dw s_ab dd s_abc dd _DATA ends 400 100000 65530 0 ; aici vom pune a+b 0 ; aici vom pune a+b+c

_TEXT segment start: mov bx, _DATA mov ds, bx _DATA mai

; de aici incepe executia programului ; incarcam in ds adresa de segment a segmentului ; de date. Nu putem face direct (mov ds, ; ; nu are operanzi compatibili (vom vedea tarziu de ce.

mov add add mov mov mov cwd add adc mov mov

cx, word cx, word bx, word word ptr word ptr ax, c

; de aici incepe programul propriu-zis ptr a ptr b ptr b+2 s_ab, cx s_ab+2, bx ; bx:cx=s_ab=a+b

cx, ax bx, dx word ptr s_abc, bx ; bx:cx=s_abc=a+b+c word ptr s_abc+2, cx ; terminam executia programului

mov ax, 4C00h int 21h _TEXT ends _STACK segment stack db 500 dup(?) _STACK ends

end start

; aici marcam punctul d intrare in progrm

Evaluarea expresiilor aritmetice


Instructiuni folosite (a se vedea Norton Guide pentru detalii): mov, add, sub, cbw, cwd, adc, sbb, mul, imul, div, idiv. Registrii de 8 biti: ah, al, bh, bl, ch, cl, dh, dl. Registrii de 16 biti: ax, bx, cx, dx, si, di. Atentie! Registrul ax este format prin juxtapunerea registrilor ah si al. Asta inseamna ca modificarea continutului unuia din registrii ah sau al conduce la modificarea bitilor corespunzatori din ax si reciproc. De exemplu, in urma instructiunilor
mov al, 10 ;10 = 0Ah mov ah, 1 registrul ax va contine valoarea

266 (010Ah).

Declaratii de date: db (1 octet = 8 biti), dw (2 octeti = 16 biti), dd (4 octeti = 32 biti).

Adunari si scaderi
Putem folosi oricare dintre registrii numiti mai sus, precum si variabile auxiliare. Exemplu: sa se evalueze expresia r=(a+b)-(c+d)-(e+20) unde toate veriabilele sunt reprezentate pe 16 biti cu semn. Rezolvare Date:
a b c d e r dw dw dw dw dw dw 1000 500 20 300 -300 ?

Cod:
mov add mov add sub ax, ax, si, si, ax, a b ; ax = a+b c d ; si = c+d si ; ax = (a+b) - (c+d)

mov add sub mov

si, e si, 20 ; si = e+20 ax, si ; ax = (a+b) - (c+d) -(e+20) r, ax

Aritmetica pe 32 de biti
Daca avem de adunat/scazut numere reprezentate pe 32 de biti (sau mai multi), nu avem instructiune de adunare/scadere corespunzatoare. In scopul efectuarii operatiei, vom aduna cate o "felie" de 16 biti o data. Astfel, vom aduna cei 16 biti mai putin semnificativi din cei doi operanzi, apoi vom aduna cei 16 biti mai semnificativi din cei doi operanzi impreuna cu transportul de la "felia" anterioara. O a doua problema ce apare este faptul ca nu putem transfera 32 de biti o data. Fie urmatorul exemplu: sa se calculeze r=a+b, unde r, a si b sunt reprezentate pe 32 de biti. Rezolvare Date:
a dd 1000000 b dd 500000 r dd ?

Cod:
mov mov add adc mov mov ax, word dx, word ax, word dx, word word ptr word ptr ptr a ptr a +2 ptr b ptr b+2 r, ax r+2, dx

Constructii folosite:
word ptr a desemneaza un obiect din memorie, de 2 octeti (word) de la lui a (ptr a). Acestia sunt de fapt cei mai putin semnificativi 16 biti din

adresa

componenta lui a. word ptr a+2 desemneaza un obiect din memorie, tot de 2 octeti (word) de la adresa egala cu adresa lui a plus 2 (ptr a +2). Acestia sunt octetii al treilea si al patrulea din componenta lui a, adica cei mai semnificativi 16 biti. instructiunea add are ca efect retinerea transportului in bistabilul CF (carry flag). instructiunea adc aduna oeranzii plus valoarea lui CF. De asemenea, noul transport generat in urma adunarii este scris in CF.

Exercitii

1. Calculati suma r=a+b, adunand doar cate 8 biti o data. 2. Calculati r=a+b-c-20, r, a,b si c fiind reprezentate pe 32 de biti cu semn.

Conversii de lungime
Daca operanzii pentru adunare si scadere nu au lungimile egale este necesara modificarea lungimii reprezentarii cel putin pentru unul dintre ei. Lungirea reprezentarii pentru intregii cu semn se face cu ajutorul instructiunilor cbw si cwd. Lungirea reprezentarilor pentru numere fara semn se face prin completare cu zerouri. Exemple 1. r=a-b, r si a fiind reprezentate pe 16 de biti cu semn, iar b pe 8 biti cu semn Rezolvare Date:
a dw 2000 b db -4 r dw ?

Cod:
mov cbw mov sub mov al, b ; ax = b si, a si, ax ; si = a-b r, si

2. r=a+b, r si b fiind pe 16 biti fara semn, iar a pe 8 biti fara semn. Rezolvare Date:
a dw 2000 b db 4 r dw ?

Cod:
mov mov add mov al, b ah, 0 ; ax = b ax, b ; ax = a+b r, ax

Exercitii 1. Calculati r=a+b-c, unde r si b sunt pe 32 de biti cu semn, a este pe 16 biti cu semn, iar c este pe 8 biti cu semn. 2. Ca la exercitiul precedent, dar numerele sunt reprezentate fara semn.

Inmultiri si impartiri
Trebuie avute in vedere restrictiile de lungime, registrii specifici pentru unii operanzi, precum si diferenta intre instructiunile pentru numere reprezentate cu semn (imul si idiv) si fara semn (mul si div). Exemplu Sa se evalueze r=(a-b*c)/d, unde a, b, c, d, r vor fi reprezentate pe 16 biti cu semn. Rezolvare Date
a b c d r dw dw dw dw dw 1000 -500 30000 10000 ?

Cod
mov ax, b imul c ; dx:ax = b*c mov bx, dx mov cx, ax ; bx:cx = b*c , avem nevoie de dx:ax pt. conversia lui a mov ax, a cwd ; dx:ax = a sub ax, cx sbb dx, bx ; dx:ax = a-b*c idiv d ; ax = (a-b*c)/d mov r, ax

Instructiuni conditionale
In limbajul de asamblare nu avem instructiuni conditionale sau bucle structurate. Acestea trebuie deci simulate folosind instructiuni gen goto sau if ... goto. Saltul neconditionat se face prin instructiunea jmp. Sintaxa este jmp eticheta. Efectul este ca urmatoarea instructiune executata este cea care urmeaza punctului marcat prin eticheta. Eticheta are sintaxa: eticheta: (numele urmat de caracterul doua-puncte).

Exista instructiuni de salt conditionat. Conditia este intotdeauna formulata in termeni de valorile bistabililor de conditie (flags). Flagurile se comporta ca niste registrii de un bit. De regula, instructiunile aritmetice si logice le modifica; celelalte instructiuni nu le modifica. Atentie: instructiunile inc si dec, desi aritmetice, nu modifica flagurile. Astfel, o instructiune de salt conditionat trebuie in principiu sa urmeze dupa o instructiune aritmetica sau logica care seteaza flag-urile. Tipuri de teste: 1. Test de zero: toate operatiile aritmetice si logice actualizeaza valoarea lui ZF (zero flag). Astfel, jz va efectua saltul daca rezultatul operatiei aritmetice anterioare a fost egal cu 0. 2. Test de depasire: pentru adunare si scadere cu numere reprezentate fara semn, avem depasire daca si nuai daca avem transport/imprumut. Astfel, testul de depasire se va face cu instructiunea jc (negata jnc). La aritmetica in cod complementar, depasirea este semnalata de OF (Overflow Flag), instructiunea de salt fiind jo 3. Comparatia: se incepe cu o operatie de scadere (sub) sau scaderea fara memorarea restului (cmp). Starea flagurilor CF si ZF (la operanzi fara semn) sau SF si ZF (la cele cu semn) permit sa aflam daca descazutul a fost mai mare, egal sau mai mic decat scazatorul. Pentru operanzi fara semn, instructiunile de salt conditionat folosite sunt jb, ja, je, jne, jbe, jae Pentru operanzi cu semn, instructiunile de salt conditionat folosite sunt jl, jg, je, jne, jle, jge. Exercitii 1. Calculati expresia r=a+b-c, numerele fiind reprezentate pe 16 biti fara semn, testand eventualele depasiri. Intr-o variabila de tip byte puneti 0 daca nu au fost depasiri si 1 daca au fost. Rezolvare Codul:
mov ax, a add ax, b jc depasire sub ax, c jc depasire mov r, ax

mov al, 0 mov dep, 0 jmp final depasire: mov al, 1 mov dep, al final:

2. Calculati cel mai mare divizor comun a doua numere date. Rezolvare 3. Aflati numarul de divizori ai unui numar dat ==

Tablouri
Declararea tablourilor
Variabilele se declara prin sintaxa: [nume] db|dw|dd valoare [, valoare ...] Numele este optional. Daca dam o singura valoare, variabila este simpla. Daca dam mai multe valori, variabila este de fapt de tip tablou. Asta inseamna ca:

asamblorul rezerva cate 1, 2 sau 4 octeti (in functie de declarare, db, dw, respectiv dd) pentru fiecare valoare data. Acestia sunt rezervati unul dupa altul, fara spatii. acestia sunt initializati cu valorile date numele, daca este prezent, desemneaza intotdeauna adresa de inceput a zonei de memorie. Prin urmare, prin acel nume se poate referi direct doar primul element. Pentru restul este necesar sa calculam adresa de inceput a elementului dorit.

Variabilele de tip db pot fi initializate cu un caracter sau sir de caractere date intre caractere apostrof. Variabilele de orice tip pot fi initializate cu constructia numar dup (valoare) aceasta fiind echivalenta cu un sir de lungime numar de valori identice cu valoare. Aceste constructii pot fi combinate. Exemple
a dw 10, 13, 16, 3000 ; tablou de 4 cuvinte (8 octeti) cu ; valorile initiale date b db 'Text', 10, 13 ; tablou de 6 octeti c db 84, 101, 120, 116, 10, 13 ; tablou de 6 octeti, avand acelasi ; continut ca si b d dd 100 dup (1) ; tablou de 100 dublu-cuvinte (400 octeti) de

noualea

; valoare 1. Primul octet, al cincilea, al

; si asa mai departe (din 4 in 4) au valoarea 1, ; ceilalti 0 e dw 50 dup (?) ; 50 cuvinte (100 octeti) cu valoare initiala ; neprecizata

In toate cazurile, numele dat variabilelor este de fapt o eticheta asociata adresei de inceput a variabilei.

Folosirea tablourilor
In toate instructiunile in care putem da ca operand o locatie de memorie putem pune si un element dintr-un tablou. De fapt, operandul este locatia de memorie de la adresa data in instructiune (daca operandul este o variabila simpla, adresa este de obicei o constanta; daca operandul este un element dintr-un tablou, adresa se calculeaza ca adresa de inceput plus indexul inmultit cu dimensiunea elementului (indexul fiind considerat de la 0). Adresa poate fi data ca:

o constanta valoarea unuia din regisrtii si, di, bx, bp, plus eventual o constanta suma dintre: si sau di, bx sau bp, si eventual o constanta

Constanta este de obicei adresa unei etichete. Exemple 1. Se cere insumarea elementelor unui tablou Rezolvare 1 Rezolvare 2 Aici numarul elementelor este calculat pornind de la diferenta intre adresa tabloului si adresa obiectului situat in memorie imediat dupa Rezolvare 3 Aici tinem minte direct adresa elementului curent, in loc de index. 2. Sa se genereze reprezentarea zecimala a unui numar dat. Rezolvare Exercitii 1. Sa se scrie un program care construieste oglinditul unui sir de caractere 2. Sa se inverseze un sir de caractere (adica la fel ca in problema precedenta, dar rezultatul se genereaza in aceeasi variabila (se scrie peste). 3. Se da reprezentarea zecimala a unui numar. Sa se calculeze numarul.

Operatii pe biti

Uneori avem nevoie sa testam sau sa modificam doar valoarea unui singur bit din cadrul unui octet, sau in general sa efectuam anumite operatii doar asupra unora dintre bitii unui octet. In acest scop avem urmatoarele operatii:
and, or, xor, not, efectueaza operatia opearnzi (la and, or si xor, fiecare bit

logica corespunzatoare la nivel de bit, intre din rezultat este rezultatul operatiei respective intre bitii de acel rang din operanzi). test este un and care nu scrie rezultatul in operandul destinatie ci doar actualizeaza flagurile (analog cu cmp fata de sub) shl, shr, sar, rol, ror, rcl, rcr deplaseaza bitii din primul operand (care trebuie sa fie registru sau locatie de memorie). Al doilea operand poate fi o constanta sau valoarea din registrul cl. not are un operand de tip registru sau locatie de memorie si inverseaza toti bitii din operand.

Exemple 1. Se cere obtinerea aceluiasi efect ca cel al operatiei cbw. Rezolvare


0 test al, 10000000b ; rezultatul va fi 0 daca bitul de semn este

2.

jz pozitiv ; daca ajungem aici, numarul este negativ mov ah, 0FFh jmp final pozitiv: mov ah,0 final: Se da o variabila, a, de tip word. Se cere sa se numere

bitii de 1.

Rezolvare 1 Vom scoate unul cate unul bitii din ax


mov ax, a mov dx, 0 ; mov cx, 0 ; bucla: shr ax, 1 ; adc dx, 0 ; add cx, 1 cmp cx, 16 jb bucla in dx vom numara bitii nr. de iteratii efectuate CF=bitul cel mai putin semnificativ din ax dx:=dx+CF

Rezolvare 2

Vom izola de fiecare data cate un bit din a


mov ax, 1 ; cele 16 ; mov cx, 0 ; mov dx, 0 ; bucla: test a, ax jz zero add dx, 1 zero: add cx, 1 cmp cx, 16 jb bucla ax va avea un singur bit 1 care va parcurge toate pozitii nr. de iteratii nr. de biti 1

3. Reprezentarea BCD a unui numar se face plecand de la reprezentarea numarului in baza 10 si alocand cate 4 biti pentru fecare cifra zecimala (in acest fel, un octet va contine cate doua cifre zecimale. De exemplu, numarul 291 se poate reprezenta in format BCD pe 2 octeti in modul urmator:
00000010 10010001

Se cere un program care sa efectueze conversia intre reprezentarea ca sir de cifre zecimale si reprezentarea BCD Rezolvare Date:
sir_cifre db '16234' bcd db (bcd-sir_cifre+1)/2 dup (?)

Codul:
mov ax, bcd-sir_cifre mov dx, 0 mov bx, 2 div bx mov cx, ax ; cx=nr. de perechi de cifre mov si, offset sir_cifre ; si = adresa cifrei curente mov di, offset bcd ; di=adresa perechii curente cmp dx, 0 je par ; daca nr. de cifre zecimale este impar, prima o tratam special mov al, byte ptr [si] sub al, '0' mov byte ptr [di], al inc si inc di par:

cmp cx, 0 je final mov ah, byte ptr [si] inc si mov al, byte ptr [si] inc si sub ah, '0' sub al, '0' ; avem acum cifrele in ah si al. Trebuie aduse ambele in al, cea din ; ah va ocupa primii 4 biti din al shl ah, 4 or al, ah mov byte ptr [di], al inc di dec cx jmp par final:

Observatii 1. O metoda de a pune valoarea 0 intr-un registru este sa se faca xor cu el insusi (exemplu xor ax, ax pune 0 in ax). 2. shl si shr pot fi folosite pentru inmultirea, repspectiv impartirea la 2. De exemplu, shl ax, 1 inmulteste cu 2 valoarea din ax. Cum se imparte la 2 un numar reprezentat cu semn? 3. Spunem ca izolam anumiti bitii dintr-un operand daca punem o vaoare fixata (de obicei 0) in toti ceilalti biti. Aceasta se face prin and intre operand si o vaoare, numita masca, ce are 1 pe pozitiile de izoat si 0 in rest. Exercitii 1. Sa se verifice daca un numar reprezenat pe 16 biti cu semn poate fi reprezentat pe 8 biti cu semn 2. Fie doua variabile, a si b, de tip word. Sa se inlocuiasca bitii 2..5 din a cu bitii 0..3 din b 3. Se da un numar in eprezentare BCD, Sa se calculeze reprezentarea binara (obsnuita) =======