Sunteți pe pagina 1din 3

Limbaje de asamblare – laborator 3

Dacă în precedenta lucrare ne-am ocupat de scrierea unor valori numerice


hexazecimale, în aceasta vom aborda numere în format zecimal. Aceasta se face prin împărțiri
succesive la 10 până obținem câtul 0, reținând resturile și scriindu-le apoi în ordinea inversă a
apariției.
Introducem și conceptul de macroinstrucțiune dar fără a insista prea mult pe el.
Aceasta, numită pe scurt macro, este o forma de prescurtare. Atunci când întâlnește numele
macro-ului asemblerul îl înlocuiește cu conținutul acestuia. Poate avea parametri și simboluri
locale, însă fără ele rezultă ceva destul de simplu și ușor de înțeles:
pushxregs MACRO
push ax
push bx
push cx
push dx
ENDM
popxregs MACRO
pop dx
pop cx
pop bx
pop ax
ENDM
Acestea pun în respectiv scot din stivă toți regiștrii de uz general, și se pot folosi la
începutul și sfârșitul unei proceduri pentru a le salva și restaura conținutul. Așa cum este această
procedură care afișează un număr zecimal fără semn pe un octet.
PrintByte PROC
;input=AL
pushxregs
xor ah, ah ; AH = 0
xor cx, cx ; CX = 0
xor dx, dx ; DX = 0
mov bl, 10
PrintByte_1:
div bl ; impartire AX cu 10
mov dl, ah ; copiem AH in DL
xor ah, ah ; AH = 0
push dx ; DX in stiva (DL cu el)
inc cx ; incrementam CX
test al, al
jnz PrintByte_1 ; salt daca AL diferit de 0
PrintByte_2:
mov ah, 2
pop dx ; scoatem DX (si DL) din stiva
add dl, '0' ; transformam numarul in cod ASCII
int 21h ; afisam
loop PrintByte_2 ; ciclu de CX ori
popxregs
ret
PrintByte ENDP
Am folosit aici instrucțiunea DIV, care în varianta pe 8 biți împarte AX la parametrul
pe 8 biți, pune câtul în AL și restul în AH. Am mai folosit și LOOP care decrementează CX și
execută salt la adresa din parametru dacă CX nu este zero. Pentru numere pe 16 biți folosim
varianta respectivă a lui DIV, care împarte dublul cuvânt din DX:AX la parametru și pune câtul
în AX și restul în DX.
OBS. Am folosit TEST AL, AL deși CMP AL, 0 ar fi fost mai intuitivă. Deoarece nu
conține nici un operand imediat, prima variantă este mai eficientă. Același lucru se poate spune
despre XOR AH, AH versus MOV AH, 0.

PrintWord PROC
;input=AX
pushxregs
xor cx, cx
xor dx, dx
mov bx, 10
PrintWord_1:
div bx
push dx
xor dx, dx
inc cx
test ax, ax
jnz PrintWord_1
PrintWord_2:
mov ah, 2
pop dx
add dl, '0'
int 21h
loop PrintWord_2
popxregs
ret
PrintWord ENDP

OBS. Instrucțiunea DIV poate genera o eroare divide overflow dacă rezultatele, câtul
de fapt, nu încape în AX (sau AL la varianta pe 8 biți). Aceasta se întâmplă dacă numărul de
împărțit este mai mare sau egal cu 655360 și se împarte la 10. Aici este imposibil să se întâmple
așa ceva deoarece cei 16 biți superiori ai DX:AX, adică DX sunt zero.

Pentru 32 biți am putea folosi instrucțiunile 386+ și regiștrii pe 32 biți (EAX, EBX etc.),
dar dacă ținem să rămânem la instrucțiuni pe 16 biți va trebui să facem o procedură specială
pentru împărțirea cu 10.
DDIV10 PROC
;input: DX:AX
;output: quotient=DX:AX, remainder=BX
push bp
mov bp, sp
sub sp, 4
mov [bp-2], ax
mov bx, 10
mov ax, dx
xor dx, dx
div bx
mov [bp-4], ax
mov ax, [bp-2]
div bx
mov bx, dx
mov dx, [bp-4]
mov sp, bp
pop bp
ret
DDIV10 ENDP

Acum că avem împărțire cu 10 a unei valori pe 32 de biți o putem folosi:


PrintDword PROC
;input: DX:AX
pushxregs
xor cx, cx
PrintDword_1:
call DDIV10
push bx
inc cx
test ax, ax
jnz PrintDword_1
test dx, dx
jnz PrintDword_1
PrintDword_2:
mov ah, 2
pop dx
add dl, '0'
int 21h
loop PrintDword_2
popxregs
ret
PrintDword ENDP

Observați două salturi în prima buclă. Primul se execută dacă AX este nonzero, al doilea
dacă DX este diferit de zero și sunt la aceeași adresă. Împreună execută salt la PrintDword_1
dacă AX sau DX sunt diferite de zero, ceea ce este același lucru cu a spune că numărul pe 32
biți din DX:AX este nonzero.

Proceduri pentru numere cu semn sunt simplu de scris dacă le avem pe cele fără semn.
Trebuie să vedem în primul rând dacă numărul este negativ - adică bitul cel mai semnificativ
este 1, și în acest caz scriem - (minus) și inversăm semnul cu instrucțiunea NEG pe 8 sau 16
biți. Pe 32 biți ne folosim de definiția complementului față de 2 și folosim 2 instrucțiuni NOT
urmate de ADD și ADC pentru a aduna 1 la rezultat. Apoi apelăm varianta fără semn.

TEMĂ. Copiați toate secvențele de cod din acest document într-un fișier LAB3.INC,
copiați acolo și procedura PrintNewline din laboratorul precedent. Scrieți un fișier LAB3.ASM
care să încludă LAB3.INC și să folosească procedurile de acolo. Fișierul LAB3.ASM ar putea să
arate așa:
model small
.stack 1024
.data
.code
include lab3.inc
start:
mov ax, @data
mov ds, ax

mov dx, 8000h


mov ax, 0
call PrintDword

call PrintNewline
mov ax, 4c00h
int 21h
end start

Rularea acestuia așa cum e scris aici, cu DX:AX=80000000h ar trebui să arate 231 =
2147483648.

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