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.
; de aici incepe programul propriu-zis
mov
add
mov
add
mov

al, a
al, b
s_ab, al
al, c
s_abc, al

mov ax, 4C00h


int 21h
_TEXT ends

; al=s_ab=a+b
; al=s_abc=a+b+c
; terminam executia programului

_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:
nume

td

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,

mov
add
add
mov
mov

cx, word
cx, word
bx, word
word ptr
word ptr

mov
cwd
add
adc
mov
mov

ax, c

nu are operanzi compatibili (vom vedea

tarziu de ce.

; 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

mov ax, 4C00h


int 21h
_TEXT ends
_STACK segment stack
db 500 dup(?)
_STACK ends

; terminam executia programului

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

2.

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

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

Rezolvare 2

in dx vom numara bitii


nr. de iteratii efectuate
CF=bitul cel mai putin semnificativ din ax
dx:=dx+CF

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)
=======

S-ar putea să vă placă și