Documente Academic
Documente Profesional
Documente Cultură
de asamblare – laborator 6
Instrucțiuni de prelucrare șiruri
Alte exemple
Funcția strcpy din C/C++ copiază un șir terminat cu zero în altul, presupunând că există
destul spațiu la destinație pentru a cuprinde șirul inițial cu tot cu terminator. Presupunem că
DS:SI și ES:DI sunt adresele sursă și destinație, atunci:
strcpy PROC
cld ; DF = 0
@@et1:
lodsb ; DS:[SI] -> AL
test al, al ; ZF=0 daca AL=0
jz @@et2
stosb ; AL -> ES:[DI]
jmp @@et1
@@et2:
stosb ; copiem si terminatorul
ret
strcpy ENDP
Directiva LOCALS @@ trebuie să fie înainte de procedură, de obicei este prima linie din
program. Ca observație, atunci când încărcarea unui registru sau o comparație se pot face
evitând o instrucțiune cu operand imediat este preferabil să se facă asta pentru că rezultatul este
mai rapid și codul mai mic. De exemplu pentru a compara AL cu 0 am folosit test al, al
nu cmp al, 0 pentru că prima nu are operand imediat ci doar regiștri.
Funcția strlen din C/C++ dă lungimea unui șir de octeți terminat cu zero, excluzând
terminatorul. Rezultatul îl vom da în AX, la fel ca în C:
strlen PROC
xor cx, cx ; CX = 0
xor al, al ; AL = 0
cld ; DF = 0
repne scasb ; repeta pana ES:[DI]=0 sau CX=0
mov ax, 0FFFFh ; AX=65535
sub ax, cx ; AX=65535-CX
ret
strlen ENDP
Valoarea 65536–CX este lungimea șirului ca urmare a repne scasb incluzând
terminatorul, asta presupunând că s-a găsit un zero în zona de memorie scanată altfel rezultatul
este irelevant. Deci lungimea șirului este 65535–CX. Aceeași observație ca la exemplul
precedent: am folosit xor cx, cx și xor al, al pentru a încărca acești regiștri cu zero. Dar
când a fost vorba de a pune 65535 în AX atunci am preferat varianta cu operand imediat, asta
pentru că nu există o singură instrucțiune care să pună 65535 într-un registru. Se putea folosi
xor ax, ax urmat de dec ax, însă asta înseamnă două instrucțiuni.
Compararea a două șiruri în C/C++ se face cu funcția strcmp. Aceasta are ca parametri
adresele de început a două șiruri terminate cu zero, sir1 și sir2, și returnează o valoare negativă
dacă sir1<sir2, zero dacă sunt egale și pozitivă dacă sir1>sir2. Noi preferăm însă o variantă mai
puțin portabilă, însă mai rapidă fiindcă poate fi folosită direct cu instrucțiuni de salt condiționat
gen JB, JE, JA, JBE, JAE: ZF=1 dacă sunt egale, altfel ZF=0 și CF=1 dacă sir1<sir2 respectiv
CF=0 dacă sir1>sir2. Ca și cum am compara două numere fără semn cu CMP.
strcmp PROC
xor cx, cx ; CX = 0
cld ; DF = 0
@@et0:
cmp byte ptr ds:[si], 0
jne @@et1
cmp byte ptr es:[di], 0
je @@exit ; ambele siruri se termina si sunt egale
;; sir1<sir2, sir1 se termina
stc ; CF=1, avem ZF=0 de la JE
ret
@@et1:
cmp byte ptr es:[di], 0
jne @@et2
;; sir1>sir2, sir2 se termina
xor al, al ; AL=0, ZF=1, CF=0
inc al ; AL=1, ZF=0, CF=0
ret
@@et2:
cmpsb
loope @@et0
@@exit:
ret
strcmp ENDP
Observați că problema asta este un pic mai complicată, asta deoarece nu știm de la
început care șir este mai lung și când să terminăm algoritmul. Se observă și că avem mai multe
puncte de ieșire din procedură cu ret. Este mai avantajos decât ar fi jmp @@exit, însă trebuie
avut grijă la astfel de proceduri să se termine în aceleași condiții. De ex. situația se complică
dacă impunem ca procedura trebuie să prezerve regiștri ca SI și DI, atunci ar fi mai avantajos
saltul spre sfârșit.
inițializare
NU sir1 se DA
termina?
sir2 se DA sir2 se DA
termina? termina?
NU NU
ZF=0, CF=0 ZF=1
ZF=0, CF=1
DA
DS:[SI]≠ES[DI]?
NU
ZF=0, CF=?
inc SI, DI; dec CX
NU
CX=0?
DA
terminare anormală