Sunteți pe pagina 1din 11

Laboratorul 6. Rezolvări.

Laboratorul 6. Rezolvări.
1. Realizați un macro care definește în zona de date un sir de caractere de forma:
"ABBCCCDDDD ... IIIIIIIIIJJJJJJJJJJ" (1xA 2xB 3xC .... 10xJ). Afișați șirul de
caractere obținut.
Rezolvare:
include \masm32\include64\masm64rt.inc

.data
sir1 LABEL BYTE ; eticheta sir1 (adresa de inceput)
FOR cnt, <1,2,3,4,5,6,7,8,9,10> ;; FOR arg = 1..10
BYTE cnt DUP (cnt-1+'A') ;; declar "arg" BYTEs initializati cu
ENDM ;; cu val: arg-1+'A' ('A'..'J')
BYTE 0 ; terminatorul de sir

sir2 LABEL BYTE ; eticheta sir2 (adresa de inceput)


k = 0
REPEAT 10 ;; repetam de 10 ori
BYTE k+1 DUP (k+'A') ;; declar "arg+1" BYTEs initializati
k = k + 1 ;; cu val: arg+'A' ('A'..'J')
ENDM
BYTE 0 ;; terminatorul de sir

sir3 LABEL BYTE ;; eticheta sir1 (adresa de inceput)


FORC arg, <ABCDEFGHIJ> ;; FORC arg = A..J
BYTE '&arg'-'A'+1 DUP ('&arg') ;; declar "'&arg'-'A'+1" BYTEs
ENDM ;; initializati cu val: '&arg'
BYTE 0 ; terminatorul de sir
crlf BYTE 13, 10, 0 ; CR+CF+0 = "\n"

.code
main proc
invoke StdOut, addr sir1 ; afisare sir1
invoke StdOut, addr crlf ; afisare "\n"
invoke StdOut, addr sir2 ; afisare sir2
invoke StdOut, addr crlf ; afisare "\n"
invoke StdOut, addr sir3 ; afisare sir3
invoke ExitProcess, 0 ; invoke ExitProcess API
ret
main endp
end

2. Realizați o funcție de conversie dintr-un șir de caractere într-un număr pe 64 de biți fără
semn. Funcția va fi conformă Microsoft x64 calling convention și se va numi „_atou” și
va primi ca parametri: pointer la șir și pointer (referință) la număr.
Rezolvare:
include \masm32\include64\masm64rt.inc

1
Programare în limbaj de asamblare (PLA)

.data
sir db "9223372036854775807", 0
nr dq ?

NOSTACKFRAME
.code
main proc
sub rsp, 32+8 ; alocare shadow area + aliniere stiva la 16o
lea rcx, sir ; RCX = primul parametru
lea rdx, nr ; RDX = al doilea parametru
call _atou ; apel procedura
add rsp, 32+8 ; eliberare stiva alocata
ret
main endp

_atou Proc
; Argumente: RCX = pointer la sir, RDX = referinta la QWORD
; Procedura nu apeleaza alte proceduri => nu mai alocam shadow area
; Procedura va returna FLAG-urile:
; PF & ZF vor reflecta valoarea returnata
; CF = 0
; OF = 1 > o valoare in afara gamei: 0 .. (2^64)-1
; val. returnata va fi 0 daca OF = 1.
mov [rsp+8], rcx ; salvare Arg.1 in shadow area
mov [rsp+16], rdx ; salvare Arg.2 in shadow area
xor r8, r8 ; R8 = 0, va contine valoarea cifrei
xor rax, rax ; R9 = 0 = suma partiala
@@:
mov r8b, [rcx] ; R8B = caracterul curent din sir
cmp r8b, '0' ; comparatie cu '0'
jb @F ; nu e caracter daca < '0' (inclusiv '\0')
cmp r8b, '9' ; comparatie cu '9'
ja @F ; nu e caracter daca > '9'
sub r8b, '0' ; in RAX conversie ASCII -> cifra
mov rdx, 10 ; RDX = 10
mul rdx ; RDX:RAX = RAX * 10 (suma *= 10)
jo @@overflow ; daca OF=1 produsul e prea mare (RDX!=0)
add rax, r8 ; adaugam cifra la suma partiala
jc @@overflow ; daca CF=1 suma partiala e prea mare
inc rcx ; incrementare pointer sir
jmp @B ; urmatorul caracter
@@:
jmp @F ; salt peste secventa de overflow
@@overflow: ; daca detectam depasire
xor eax, eax ; rezultatul = 0 in cazul depasirii
pushf ; Push FLAGS pe stiva
bts word ptr [rsp], 11 ; set bitul 11 din cuvantul din stiva
popf ; Pop FLAGS
@@:
mov rcx, [rsp+16] ; RCX = referinta la nr de returnat
mov [rcx], rax ; salvam rezultatul in Arg.2

2
Laboratorul 6. Rezolvări.

ret ; revenire din procedura


_atou EndP
end

3. Similar realizați o funcție „_atoi” de conversie dintr-un șir de caractere într-un număr
pe 64 de biți cu semn.
Rezolvare:
_atoi Proc
; argumente: RCX = pointer la sir, RDX = referinta la QWORD
; Procedura nu apeleaza alte proceduri => nu mai alocam shadow area
; Procedura va returna FLAG-urile:
; PF & ZF vor reflecta valoarea returnata
; CF = 0
; OF = 1 -> o valoare in afara gamei: -(2^63) .. (2^63)-1
; val. returnata va fi 0 daca OF = 1.
mov [rsp+8], rcx ; salvare arg 1 in shadow area
mov [rsp+16], rdx ; salvare arg 2 in shadow area
xor r8, r8 ; R8 = 0, va contine valoarea cifrei
xor rax, rax ; RAX = 0 = suma partiala
xor r9b, r9b ; R9B = 0 (salvam semnul)
mov r8b, [rcx] ; R8B = primul caracter din sir
cmp r8b, '-' ; sirul incepe cu '-'?
jne @F ; daca nu skip
not r9b ; altfel R9B = !R9B
inc rcx ; RCX++ incrementare pointer la sir
@@:
mov r8b, [rcx] ; R8B = caracterul curent din sir
cmp r8b, '0' ; comparatie cu '0'
jb @F ; nu e caracter daca < '0' (inclusiv '\0')
cmp r8b, '9' ; comparatie cu '9'
ja @F ; nu e caracter daca > '9'
sub r8b, '0' ; in RAX conversie ASCII -> cifra
mov rdx, 10 ; RDX = 10
mul rdx ; RDX:RAX = RAX * 10 (suma *= 10)
jo @@overflow ; daca OF=1 produsul e prea mare (RDX!=0)
add rax, r8 ; adaugam cifra la suma partiala
jc @@overflow ; daca CF=1 suma partiala e prea mare
inc rcx ; incrementare pointer sir
jmp @B ; urmatorul caracter
@@
test r9b, r9b ; verific semnul salvat in R9B
jz @F ; daca nu avea semn -> skip
neg rax ; altfel inversam semnul numarului
test rax, rax ; testam nr "negativ" rezultat
js @F ; daca este neg. salt peste tratare overflow
@@overflow: ; eticheta locala - tratarea depasirilor
xor eax, eax ; rezultatul = 0 in cazul depasirii
pushf ; Push FLAGS pe stiva
bts word ptr [rsp], 11 ; set bitul 11 (OF) din cuvantul din stiva
popf ; Pop FLAGS

3
Programare în limbaj de asamblare (PLA)

@@:
mov rcx, [rsp+16] ; RCX = referinta la nr de returnat
mov [rcx], rax ; salvam rezultatul in param. 2
ret ; revenire din procedura
_atoi EndP

4. Realizați macro-uri denumite „atou” și „atoi” pentru apelul procedurilor anterioare.


Rezolvare:
atou MACRO sir:REQ, nr:REQ
lea rdx, nr
lea rcx, sir
call _atou
ENDM

atoi MACRO sir:REQ, nr:REQ


lea rdx, nr
lea rcx, sir
call _atoi
ENDM

5. Realizați un program care sortează un vector de numere întregi pe 32 biți definit în zona
de memorie după algoritmul bubble sort.
Rezolvare:
include \masm32\include64\masm64rt.inc

extrn putchar:proc ; Functia "C" putchar()

PutI MACRO _nr:REQ ; Macro pentru apelarea procedurii _PutI


mov rcx, _nr ; Param 1 in RCX
call _PutI ; Apelul functiei
EndM

PutCh MACRO chr:VARARG ; Macro pentru afisare secventa de caractere


FOR arg, <chr> ; cu nr. variabil de argumente (caractere)
mov cl, arg ; pune caracter in CL
call putchar ; apeleaza functia "C" putchar()
ENDM
ENDM

.data
vect QWORD 500, 333, 100, 70000, -100h, 9999, 55, -1, 0
v_len EQU ($-vect) / SIZEOF QWORD
msg_1 BYTE "Vectorul initial: ", 0
msg_2 BYTE "Vectorul rezultat: ", 0

; In comentariu: C-Like Code (inclusiv adresarea vectorilor)


; Nota: in .asm adresarea indexata se face la nivel de octet;
; pentru a adresa la nivel de WORD, DWORD sau QWORD adresa trebuie
; scalata: "vect[i]" din C se traduce adresa de baza a vectorului
; + indexul i * TYPEOF(vect) = SIZEOF(QWORD)

4
Laboratorul 6. Rezolvări.

NOSTACKFRAME
.code
main proc
sub rsp, 32+8
; *** Afisare vector initial ***
invoke StdOut, addr msg_1 ; Afisare sir msg_1
lea rbx, vect
xor rdi, rdi ; RDI = 0
@@: ; WHILE \
cmp rdi, v_len ; (RDI < v_len)
jnb @F ; {
PutI [rbx+rdi*8] ; Afisare "vect[RDI]"
PutCh ' ' ; Afisare caracterul ' '
inc rdi ; RDI++
jmp @B ; }
@@: ; END_WHILE
PutCh 13, 10 ; Afisare "\n"
; *** Ordonare vector dupa alg. BubbleSort ***
; for (i = n-1; i!=0 ; i--)
; for (j = 0; j<i; j++)
; if (arr[j] < arr[j+1])
; swap(&arr[j], &arr[j+1]);
; *** Daca implementam cu WHILE obtinem ***
; i = n-1
; while (i != 0) {
; j = 0
; while (j < i) {
; if (vect[j] < arr[j+1])
; swap(&vect[j], &vect[j+1])
; j++ }
; i-- }
lea rbx, vect ; RBX = adresa vectorului
mov rdx, v_len-1 ; RDX = v_len-1 ("i")
While1_Begin: ; WHILE \
test rdx, rdx ; (RDX != 0)
jz While1_End ; {
xor rcx, rcx ; RCX = 0 ("j")
mov r8, [rbx] ; R8 = "vect[0]"
While2_Begin: ; WHILE \
cmp rcx, rdx ; (RCX < RDX)
jnb While2_End ; {
mov r9, [rbx+rcx*8+8] ; R9 = "vect[j+1]"
cmp r8, r9 ; IF
jng @F ; (R8 > R9)
xchg r8, r9 ; { exchange R8 cu R9
mov [rbx+rcx*8], r8 ; "vect[j]" = R8
mov [rbx+rcx*8+8], r9 ; "vect[j+1]" = R9
@@:
mov r8, r9 ; R8="vect[j+1]"
inc rcx ; RCX++
jmp While2_Begin ; }

5
Programare în limbaj de asamblare (PLA)

While2_End: ; END_WHILE
dec rdx ; RDX--
jmp While1_Begin ; }
While1_End: ; END_WHILE
; *** Afisare vector rezultat ***
invoke StdOut, addr msg_2 ; Afisare sir msg_2
lea rbx, vect
xor rdi, rdi ; RDI = 0
@@: ; WHILE \
cmp rdi, v_len ; (RDI < v_len)
jnb @F ; {
PutI [rbx+rdi*8] ; Afisare "vect[RDI]"
PutCh ' ' ; Afisare caracterul ' '
inc rdi ; RDI++
jmp @B ; }
@@: ; END_WHILE
PutCh 13, 10 ; Afisare "\n"
invoke ExitProcess, 0 ; invoke ExitProcess API
add rsp, 40
ret
main endp

; Procedura de afisare a unui QWORD cu semn


_PutI proc ; parametru: numar cu semn pe 64 biti (DWORD)
push rbp ; salvare RBP (creare STACK FRAME)
mov rbp, rsp ; copiere RSP in RBP pentru acesarea param
mov [rbp+16], rcx ; salvam RCX in SHADOW AREA
sub rsp, 32+16 ; alocare SHADOW AREA + var. locala
mov rax, rcx ; RAX <- parametrul din RCX
cmp rax, 0 ; comparam cu 0
jge @F ; salt daca este mai mare sau egal
; (comparatie cu semn) => numar pozitiv
PutCh '-' ; altfel (nr. neg.) => afisare caracter '-'
mov rax, [rbp+16] ; refac RAX (RAX modif. de procedura putchar)
neg rax ; inversez semnul (RAX devine pozitiv)
@@:
xor rdx, rdx ; RDX = 0 => RDX:RAX = nr. de afisat
mov rcx, 10 ; baza de numeratie
div rcx ; RDX:RAX / baza (RAX=cat, RDX = rest)
test rax, rax ; RAX == 0 (conditia de iesire)
je @F ; salt la urmatorul @@
mov [rbp-8], dl ; DL (rest) -> var locala (stiva)
mov rcx, rax ; parametru in RCX
call _PutI ; apel recursiv
mov dl, [rbp-8] ; DL (rest) <- var locala (stiva)
@@:
add dl, '0' ; conversie cifra din DL in caracter ASCII
PutCh dl ; afisare cifra din DL
add rsp, 32+16 ; eliberare stiva
pop rbp
ret

6
Laboratorul 6. Rezolvări.

_PutI EndP
end

6. Modificați programul pentru a permite citirea vectorului de numere de la tastatură.


Rezolvare:
În rezolvarea problemei a fort utilizată o variantă modificată a procedurii _atoi
implementată în problema 3. Varianta aceasta primește ca parametru pe stivă doar pointer la șirul
de convertit și returnează numărul în EAX. În plus aceasta setează flag-ul de overflow și în cazul
în care șirul introdus nu conține nici o cifră validă.
include \masm32\include64\masm64rt.inc

extrn putchar:proc ; Functia "C" putchar()

PutI MACRO _nr:REQ ; Macro pentru apelarea procedurii _PutI


mov rcx, _nr ; Param 1 in RCX
call _PutI ; Apelul functiei
EndM

AtoI MACRO p_str:REQ ; Macro pentru apelarea procedurii _AtoI


lea rcx, p_str ; Param 1 in RCX (pointer)
call _AtoI ; Apelul functiei
EndM

PutCh MACRO chr:VARARG ; Macro pentru afisare secventa de caractere


FOR arg, <chr> ; cu nr. variabil de argumente (caractere)
mov cl, arg ; pune caracter in CL
call putchar ; apeleaza functia "C" putchar()
ENDM
ENDM

.data
VMAX EQU 25 ; constanta pt dim. maxima pt vect
vect QWORD VMAX DUP (?) ; vector de "VMAX" DWORDs
v_len QWORD ? ; dimensiune vectorului
txt_0 BYTE "Re-", 0 ; pentru Re-introducerea datelor
txt_1 BYTE "Introduceti dimensiunea vectorului [2-", 0
txt_2 BYTE "Introduceti elementul [", 0
txt_X BYTE "]: ", 0 ; extra bracket
msg_1 BYTE "Vectorul initial: ", 0
msg_2 BYTE "Vectorul ordonat: ", 0
buffer DB 260 DUP(?) ; buffer pt citire de la tastatura

NOSTACKFRAME
.code
main proc
sub rsp, 32+8 ; Creaar SHADOW AREA + aliniere 16o
Citire:
invoke StdOut, addr txt_1 ; Afisare sir txt_1
PutI VMAX ; Afisare nr VMAX
invoke StdOut, addr txt_X ; Afisare "]: "

7
Programare în limbaj de asamblare (PLA)

invoke StdIn, addr buffer, 260 ; Citire de la tastatura


AtoI buffer ; RAX = conversie numar
jo ReCitire? ; daca nr nu a fost convertit corect
cmp rax, 2 ; sau daca este < 2
jl ReCitire? ; Recitim numarul
cmp rax, VMAX ; sau daca > VMAX
jg ReCitire? ; Recitim numarul
jmp @F
ReCitire?:
invoke StdOut, addr txt_0 ; Afisare sir "Re-"
jmp Citire ; Recitire
@@:
lea rbx, vect
mov v_len, rax
; *** Introducerea elementelor vectorului + validare ***
xor rdi, rdi ; initializare index
@@:
cmp rdi, v_len ; While (index < v_len)
jnb @F
Citire_NR:
invoke StdOut, addr txt_2 ; Afisare sir txt_2
PutI rdi ; Afisare index
invoke StdOut, addr txt_X ; Afisare "]: "
invoke StdIn, addr buffer, 260 ; Citire de la tastatura
AtoI buffer ; Conversie numar
jno NR_OK ; Validare nr introdus
invoke StdOut, addr txt_0 ; Afisare sir "Re-"
jmp Citire_NR ; Re-Citim sirul
NR_OK:
mov [rbx+rdi*SIZEOF QWORD], rax ; Salvare numar in "vect[i]"
inc rdi ; Incrementare index
jmp @B
@@:
PutCh 13,10

; *** Afisare vector initial ***


invoke StdOut, addr msg_1 ; Afisare sir msg_1
lea rbx, vect
xor rdi, rdi ; RDI = 0
@@: ; WHILE \
cmp rdi, v_len ; (RDI < v_len)
jnb @F ; {
PutI [rbx+rdi*8] ; Afisare "vect[RDI]"
PutCh ' ' ; Afisare caracterul ' '
inc rdi ; RDI++
jmp @B ; }
@@: ; END_WHILE
PutCh 13, 10 ; Afisare "\n"
; *** Ordonare vector dupa alg. BubbleSort ***
lea rbx, vect ; RBX = adresa vectorului
mov rdx, v_len ; RDX = v_len ("i")

8
Laboratorul 6. Rezolvări.

dec rdx
While1_Begin: ; WHILE \
test rdx, rdx ; (RDX != 0)
jz While1_End ; {
xor rcx, rcx ; RCX = 0 ("j")
mov r8, [rbx] ; R8 = "vect[0]"
While2_Begin: ; WHILE \
cmp rcx, rdx ; (RCX < RDX)
jnb While2_End ; {
mov r9, [rbx+rcx*8+8] ; R9 = "vect[j+1]"
cmp r8, r9 ; IF
jng @F ; (R8 > R9)
xchg r8, r9 ; { exchange R8 cu R9
mov [rbx+rcx*8], r8 ; "vect[j]" = R8
mov [rbx+rcx*8+8], r9 ; "vect[j+1]" = R9
@@:
mov r8, r9 ; R8="vect[j+1]"
inc rcx ; RCX++
jmp While2_Begin ; }
While2_End: ; END_WHILE
dec rdx ; RDX--
jmp While1_Begin ; }
While1_End: ; END_WHILE
; *** Afisare vector rezultat ***
invoke StdOut, addr msg_2 ; Afisare sir msg_2
lea rbx, vect
xor rdi, rdi ; RDI = 0
@@: ; WHILE \
cmp rdi, v_len ; (RDI < v_len)
jnb @F ; {
PutI [rbx+rdi*8] ; Afisare "vect[RDI]"
PutCh ' ' ; Afisare caracterul ' '
inc rdi ; RDI++
jmp @B ; }
@@: ; END_WHILE
PutCh 13, 10 ; Afisare "\n"
invoke ExitProcess, 0 ; invoke ExitProcess API
add rsp, 40
ret
main endp

; Procedura de afisare a unui QWORD cu semn


_PutI proc ; parametru: numar cu semn pe 64 biti (DWORD)
push rbp ; salvare RBP (creare STACK FRAME)
mov rbp, rsp ; copiere RSP in RBP pentru acesarea param
mov [rbp+16], rcx ; salvam RCX in SHADOW AREA
sub rsp, 32+16 ; alocare SHADOW AREA + var. locala
mov rax, rcx ; RAX <- parametrul din RCX
cmp rax, 0 ; comparam cu 0
jge @F ; salt daca este mai mare sau egal
; (comparatie cu semn) => numar pozitiv

9
Programare în limbaj de asamblare (PLA)

PutCh '-' ; altfel (nr. neg.) => afisare caracter '-'


mov rax, [rbp+16] ; refac RAX (RAX modif. de procedura putchar)
neg rax ; inversez semnul (RAX devine pozitiv)
@@:
xor rdx, rdx ; RDX = 0 => RDX:RAX = nr. de afisat
mov rcx, 10 ; baza de numeratie
div rcx ; RDX:RAX / baza (RAX=cat, RDX = rest)
test rax, rax ; RAX == 0 (conditia de iesire)
jz @F ; salt la urmatorul @@
mov [rbp-8], dl ; DL (rest) -> var locala (stiva)
mov rcx, rax ; parametru in RCX
call _PutI ; apel recursiv
mov dl, [rbp-8] ; DL (rest) <- var locala (stiva)
@@:
add dl, '0' ; conversie cifra din DL in caracter ASCII
PutCh dl ; afisare cifra din DL
add rsp, 32+16 ; eliberare stiva
pop rbp
ret
_PutI EndP

; Procedura de conversie a unui sir de caractere in QWORD cu semn


_AtoI Proc
; argumente: RCX = pointer la sir
; Procedura nu apeleaza alte proceduri => nu mai alocam shadow area
; Procedura va returna FLAG-urile:
; PF & ZF vor reflecta valoarea returnata
; CF = 0
; OF = 1 -> o valoare in afara gamei: -(2^63) .. (2^63)-1
; val. returnata va fi 0 daca OF = 1.
mov [rsp+8], rcx ; salvare arg 1 in shadow area
xor r8, r8 ; R8 = 0, va contine valoarea cifrei
xor rax, rax ; R9 = 0 = suma partiala
xor r9b, r9b ; R9B = 0 (salvam semnul)
mov r8b, [rcx] ; R8B = primul caracter din sir
test r8b, r8b ; R8b == '\0'?
jz @@overflow ; daca da, salt
cmp r8b, '-' ; sirul incepe cu '-'?
jne @F ; daca nu skip
not r9b ; altfel R9B = !R9B
inc rcx ; RCX++ incrementare pointer la sir
@@:
mov r8b, [rcx] ; R8B = caracterul curent din sir
test r8b, r8b ; daca este '\0'
jz @F ; am ajuns la finalul sirului
cmp r8b, '0' ; comparatie cu '0'
jb @@overflow ; nu e caracter daca < '0' (inclusiv '\0')
cmp r8b, '9' ; comparatie cu '9'
ja @@overflow ; nu e caracter daca > '9'
sub r8b, '0' ; in RAX conversie ASCII -> cifra
mov rdx, 10 ; RDX = 10

10
Laboratorul 6. Rezolvări.

mul rdx ; RDX:RAX = RAX * 10 (suma *= 10)


jo @@overflow ; daca OF=1 produsul e prea mare (RDX!=0)
add rax, r8 ; adaugam cifra la suma partiala
jc @@overflow ; daca CF=1 suma partiala e prea mare
inc rcx ; incrementare pointer sir
jmp @B ; urmatorul caracter
@@:
test r9b, r9b ; verific semnul salvat in R9B
jz @F ; daca nu avea semn -> skip
neg rax ; altfel inversam semnul numarului
test rax, rax ; testam nr "negativ" rezultat
js @F ; daca este neg. salt peste tratare overflow
@@overflow: ; eticheta locala - tratarea depasirilor
xor eax, eax ; rezultatul = 0 in cazul depasirii
pushf ; Push FLAGS pe stiva
bts word ptr [rsp], 11 ; set bitul 11 (OF) din cuvantul din stiva
popf ; Pop FLAGS
@@:
ret ; revenire din procedura
_AtoI EndP

end

11

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