Upload
acikamvulovic
View
44
Download
0
Embed Size (px)
DESCRIPTION
Vezba 6 - Procedure
1
6.
PROCEDURE
6.1 Povezivanje sa eksternim bibliotekama
Ako imate dovoljno vremena, moete napisati detaljan kod za ulaz/izlaz u asembleru.
Ovaj posao je zanimljiv, ali istovremeno i oduzima dosta vremena. Zbog toga, za sada,
ostaemo pri pozivima biblioteka koje ove procedure ve imaju napisane.
Biblioteka Irvine32 je za programe napisane za 32-bitni zatieni reim. Sadri procedure
koje se povezuju sa MS-Windows API-jem kada generiu ulazno/izlazne operacije. Biblioteka
Irvine16 je za programe pisane za 16-bitni reim sa realnim adresama. Sadri procedure koje
izvravaju MS-DOS prekidne rutine kada generiu ulaz-izlaz.
6.1.1 Linkerske biblioteke
Linkerska biblioteka je fajl koji sadri procedure (subrutine) koje su asemblirane u
mainski kod. Linkerska biblioteka poinje kao jedan ili vie izvornih fajlova, koji se
asembliraju u objektne fajlove. Objektni fajlovi se ubacuju u specijalno formatiran fajl kojeg
prepoznaje linker. Pretpostavimo da program prikazuje string na konzoli pozivom procedure
WriteString. Izvorni fajl programa mora sadrati direktivu PROTO koja identifikuje
WriteString proceduru:
WriteString PROTO
Potom, instrukcija CALL izvrava WriteString:
call WriteString
Kada se program asemblira, asembler ostavlja odredinu adresu CALL instrukcije praznu,
znajui da e je popuniti linker. Linker trai WriteString u bibliotekama i kopira odgovarajue
mainske instrukcije iz biblioteke u izvrni fajl programa. Dodatno, on ubacuje adresu
procedure WriteString u CALL instrukciju. Ako se procedura koju pozivate ne nalazi u
linkerskoj biblioteci, linker prijavljuje greku i ne kreira izvrni fajl.
2
6.1.2 Linkovanje 32-bitnih programa
Fajl kernel32.lib je deo Microsoft Windows Platform Software Development Kit i sadri
informacije o sistemskim funkcijama koje se nalaze u fajlu kernel32.dll. Ovaj fajl je osnovni
deo MS-Windows-a i naziva se dinamika linkerska biblioteka (dynamic link library). Sadri
izvrne funkcije koje obavljaju ulazno-izlazne operacije nad karakterima. Na slici 1 je
prikazano kako su povezani kernel32.lib i kernel32.dll.
Slika 1. Linkovanje 32-bitnih programa
Biblioteka Irvine32.lib
U tabeli 1 je prikazan skup svih procedura koje se nalaze u Irvine32 biblioteci. Za
detaljniji opis pogledajte strane 134-149 u knjizi.
Tabela 1. Procedure u biblioteci Irvine32.lib
Procedura Opis
CloseFile Zatvara fajl koji je bio prethodno otvoren.
Clrscr Brie sadraj konzole i premeta kursor u gornji levi ugao.
CreateOutputFile Kreira novi fajl za pisanje u reimu izlaza.
Crlf Upisuje novi red u konzolu.
Delay Pauzira izvravanje programa za specificirani interval od n milisekundi.
DumpRegs Prikazuje sadraj registara u heksadecimalnom formatu, kao i statusnih flegova.
GetCommandTail Kopira argumente komandne linije programa (command tail) u niz
bajtova.
GetDateTime Vraa trenutni datum i vreme iz sistema.
GetMaxXY Vraa broj kolona i vrsta u baferu konzole.
GetMseconds Vraa broj milisekundi koji je proao od ponoi.
GetTextColor Vraa aktivnu boju pozadine i teksta u konzoli.
Gotoxy Smeta kursor na odreenu vrstu i kolonu u konzoli.
IsDigit Postavlja Zero fleg ako AL registar sadri ASCII kod za decimalne brojeve (0-9).
MsgBox Prikazuje popup message box.
MsgBoxAsk Prikazuje message box sa yes/no pitanjima.
OpenInputFile Otvara postojei fajl za upis.
ParseDecimal32 Konvertuje neoznaeni decimalni ceo broj u 32-bitni binarni.
ParseInteger32 Konvertuje oznaeni decimalni ceo broj u 32-bitni binarni.
3
Random32 Generie pseudosluajni 32-bitni ceo broj u intervalu od 0 do FFFFFFFFh.
Randomize Postavlja vrednost generatora sluajnih brojeva.
RandomRange Generie pseudosluajni ceo broj unutar specificiranog opsega.
ReadChar eka dok se jedan karakter ne ukuca na tastaturi i vraa taj karakter.
ReadDec ita 32-bitni neoznaeni decimalni ceo broj sa tastature, posle koga ide Enter.
ReadFromFile Uitava sa ulaznog diska u bafer.
ReadHex ita 32-bitni heksadecimalni ceo broj sa tastature, posle koga ide Enter.
ReadInt ita 32-bitni oznaeni decimalni ceo broj sa tastature, posle koga ide Enter.
ReadKey ita karakter iz ulaznog bafera tastature bez ekanja na ulaz.
ReadString Uitava string sa tastature, posle koga ide Enter.
SetTextColor Postavlja boju pozadine i teksta svih narednih linija u konzoli.
Str_compare Poredi dva stringa.
Str_copy Kopira izvorini string u odredini.
Str_length Vraa duinu stringa u EAX.
Str_trim Uklanja neeljene karaktere iz stringa.
Str_ucase Konvertuje string u velika slova.
WaitMsg Prikazuje poruku i eka na pritisak tastera.
WriteBin Ispisuje 32-bitni neoznaeni ceo broj na konzoli u ASCII binarnom formatu.
WriteBinB Ispisuje binarni ceo broj u konzoli u formatu bajta, rei ili dvostruke rei.
WriteChar Ispisuje jedan karakter u konzoli.
WriteDec Ispisuje neoznaeni 32-bitni ceo broj u konzoli u decimalnom formatu.
WriteHex Ispisuje 32-bitni ceo broj u konzoli u heksadecimalnom formatu.
WriteHexB Ispisuje ceo broj duine bajta, rei ili dvostruke rei u konzoli u heksadecimalnom formatu.
WriteInt Ispisuje oznaeni 32-bitni ceo broj u konzoli u decimalnom formatu.
WriteStackFrame Ispisuje okvir steka trenutne procedure u konzoli.
WriteStackFrameName Ispisuje ime i okvir steka trenutne procedure u konzoli.
WriteString Ispisuje string koji se zavrava sa null u konzoli.
WriteToFile Ispisuje bafer u izlazni fajl.
WriteWindowsMsg Ispisuje string koji sadri najskoriju greku koju je generisao MS-Windows.
6.1.3 Primeri programa sa bibliotekim funkcijama
Unos i ispis celih brojeva i rad sa bojama
TITLE Library Test #1: Integer I/O (InputLoop.asm) ; Testiranje Clrscr, Crlf, DumpMem, ReadInt, SetTextColor, ; WaitMsg, WriteBin, WriteHex i WriteString procedura. INCLUDE Irvine32.inc .data
4
COUNT = 4 ; konstanta koja oznaava koliko puta se petlja ponavlja ; konstante za promenu boje prozora ; bajt za boju smeta boju pozadine u gornja 4 bita, a boju teksta u donja 4 bita ; pozadinska boja se mnoi sa 16 kako bi se pomerila u gornja 4 bita BlueTextOnGray = blue + (lightGray * 16) DefaultColor = lightGray + (black * 16) arrayD SDWORD 12345678h,1A4B2000h,3434h,7AB9h ; niz oznaenih celih dvostrukih rei prompt BYTE "Unesite 32-bitni ceo broj: ",0 .code main PROC ; Biranje plavog teksta na svetlo sivoj pozadini mov eax,BlueTextOnGray call SetTextColor ; poziv procedure za promenu boje (boja mora prethodno biti u EAX) call Clrscr ; brisanje ekrana, mora se uraditi da bi se primenile nove boje ; Prikaz niza korienjem DumpMem mov esi,OFFSET arrayD ; poetni OFFSET mov ebx,TYPE arrayD ; dvostruka re = 4 bajta mov ecx,LENGTHOF arrayD ; broj elemenata u nizu arrayD call DumpMem ; prikaz dela memorije (poetna adresa mora biti u ESI, broj elemenata u ECX, a veliina elementa u EBX) ; Upit korisniku da unese niz oznaenih celih brojeva call Crlf ; nova linija mov ecx,COUNT L1: mov edx,OFFSET prompt ; ofset stringa se premeta u EDX call WriteString ; poziv procedure za ispis stringa (ofset mora prethodno biti premeten u EDX) call ReadInt ; uitava ceo broj u EAX call Crlf ; nova linija ; Prikaz celog broja u decimalnom, heksadecimalnom i binarnom formatu call WriteInt ; prikaz oznaenog celog broja (broj mora biti u EAX) call Crlf call WriteHex ; prikaz u heksadecimalnom formatu (broj mora biti u EAX) call Crlf call WriteBin ; prikaz u binarnom formatu (broj mora biti u EAX) call Crlf call Crlf loop L1 ; ponavljanje petlje ; Vraanje konzole na podrazumevane boje call WaitMsg ; "Press any key..." mov eax,DefaultColor call SetTextColor call Clrscr exit main ENDP END main
Izlaz iz programa:
5
Generisanje sluajnih brojeva
Generie se 10 neoznaenih celih brojeva u opsegu od 0 do 4.294.967.294. Potom,
generie se 10 oznaenih celih brojeva u opsegu od -50 do +49.
TITLE Link Library Test #2 (TestLib2.asm) ; Testiranje Irvine32 bibliotekih procedura. INCLUDE Irvine32.inc TAB = 9 ; ASCII kod za Tab .code main PROC call Randomize ; inicijalizacija generatora sluajnih brojeva call Rand1 call Rand2 exit main ENDP Rand1 PROC ; Generisanje deset pseudo-sluajnih celih brojeva. mov ecx,10 ; ponavljanje petlje 10 puta L1: call Random32 ; generisanje sluajnog celog broja call WriteDec ; ispis neoznaenog decimalnog broja mov al,TAB ; horizontalni tab call WriteChar ; ispis taba loop L1 call Crlf ret Rand1 ENDP Rand2 PROC ; Generisanje deset pseudo-sluajnih celih brojeva u opsegu od -50 do +49
6
mov ecx,10 ; ponavljanje 10 puta L1: mov eax,100 ; vrednosti 0-99 call RandomRange ; generisanje sluajnog celog broja sub eax,50 ; vrednosti od -50 do +49 call WriteInt ; ispis oznaenog decimalnog broja mov al,TAB ; horizontalni tab call WriteChar ; ispis taba loop L1 call Crlf ret Rand2 ENDP END main
Izlaz iz programa:
Merenje performansi
Asemblerski jezik se esto koristi za optimizaciju delova koda koji su kritini za
performanse programa. Procedura GetMseconds iz Irvine32.lib vraa broj milisekundi od
ponoi. U ovom programu, poziva se navedena procedura, izvrava se ugnjedena petlja i
ponovo se poziva procedura. Razlika izmeu dve vrednosti koje vraa ova procedura daje
vreme izvravanja ugnjedene petlje.
TITLE Link Library Test #3 (TestLib3.asm) ; Raunanje vremena izvravanja ugnjedene petlje INCLUDE Irvine32.inc .data OUTER_LOOP_COUNT = 3 startTime DWORD ? msg1 BYTE "Molimo sacekajte...",0dh,0ah,0 msg2 BYTE "Proteklo milisekundi: ",0 .code
7
main PROC mov edx,OFFSET msg1 ; "Molimo saekajte..." call WriteString ; uvanje poetnog vremena call GetMSeconds mov startTime,eax ; Poetak spoljanje petlje mov ecx,OUTER_LOOP_COUNT L1: call innerLoop loop L1 ; Raunanje proteklog vremena call GetMSeconds sub eax,startTime ; Prikaz proteklog vremena mov edx,OFFSET msg2 ; "Proteklo milisekundi: " call WriteString call WriteDec ; ispis milisekundi call Crlf exit main ENDP innerLoop PROC push ecx ; uvanje trenutne vrednosti ECX mov ecx,0FFFFFFFh ; postavljanje brojaa petlje L1: mul eax ; pisanje nekih instrukcija da bi prolo vie vremena mul eax mul eax loop L1 ; ponavljanje unutranje petlje pop ecx ; vraanje sauvane vrednosti EAX ret innerLoop ENDP END main
Izlaz iz programa:
8
6.2 Operacije sa stekom
Ako smestimo deset ploa jednu na drugu, kao na slici 2, ono to se dobije se moe
nazivati stekom. Iako je mogue ukloniti plou iz sredine steka, ei je sluaj da se uklanja sa
vrha. Nove ploe se mogu dodavati na vrh steka, ali nikad u sredinu ili dno.
Slika 2. Stek ploa
Stek kao struktura podataka poiva na istim principima kao stek ploa: nove vrednosti se
dodaju na vrh steka, a postojee se uklanjaju isto sa vrha. U optem sluaju, stekovi su korisne
strukture za veliki broj aplikacija, a mogu se lako implementirati koristei objektno-orijentisane
programske metode. Stek se naziva i LIFO struktura (Last-In, First-Out) zato to se poslednja
vrednost koja je smetena na stek prva uzima.
Stek izvravanja programa (runtime stack) emo obraivati. Procesorski hardver ga
direktno podrava, a nezaobilazni je deo mehanizma za pozivanje i vraanje iz procedura.
Uglavnom emo ga nazivati samo stek.
6.2.1 Stek izvravanja programa
Stek izvravanja programa je memorijski niz kojim direktno upravlja procesor, koristei
ESP registar, poznat kao registar pokazivaa steka. ESP registar sadri 32-bitni ofset u nekoj
lokaciji na steku. Retko se direktno manipulie sa ESP, umesto toga, indirektno se modifikuje
instrukcijama kao to su CALL, RET, PUSH i POP.
ESP uvek ukazuje na poslednju vrednost koja je smetena na steku. Radi demonstracije,
ponimo sa stekom koji sadri jednu vrednost. Na slici 3, ESP sadri heksadecimalni broj
00001000, ofset broja koji je poslednji smeten na steku (00000006). Na naim dijagramima,
vrh steka se pomera na dole kada se smanjuje vrednost pokazivaa steka.
Slika 3. Stek koji sadri jednu vrednost
9
Svaka lokacija na steku na ovoj slici sadri 32 bita, to je sluaj kada se program pokree
u 32-bitnom reimu. U 16-bitnom reimu sa realnim adresama, registar SP pokazuje na
najskorije smetenu vrednost na steku i obino su vrednosti duine 16 bita.
6.2.2 Operacija smetanja na stek (push)
32-bitna operacija push dekrementira pokaziva steka za 4 i kopira vrednost na novu
lokaciju u steku, na koju ukazuje nova vrednost pokazivaa steka. Na slici 4 je prikazan efekat
smetanja broja 000000A5 na stek koji ve sadri jednu vrednost (00000006).
Slika 4. Smetanje brojeva na stek
Primetite da ESP registar uvek ukazuje na vrh steka. Na slici je prikazan redosled na steku
suprotan od onog sa ploama, zato to stek izvravanja programa raste sa opadajuim adresama.
Pre smetanja na stek, ESP = 00001000h, nakon toga, ESP = 00000FFCh. Na slici 5 je prikazan
isti stek nakon smetanja ukupno etiri broja.
Slika 5. Stek nakon smetanja etiri broja
6.2.3 Operacija uzimanja sa steka (pop)
Operacija pop uklanja vrednost sa steka. Nakon to je vrednost uklonjena, pokaziva
steka se inkrementira (sa veliinom elementa steka) kako bi ukazivao na sledeu popunjenu
lokaciju na steku. Na slici 6 je prikazan stek pre i posle uzimanja broja 00000002.
10
Slika 6. Uzimanje vrednosti sa steka
Prostor u steku ispod ESP je logiki prazna i preko nje e biti prepisane vrednosti kada
program sledei put bude izvravao instrukcije koje smetaju vrednosti na stek.
6.2.4 Primena steka
Postoji nekoliko vanih koristi od steka u programima:
Stek je pogodan privremeni smetajni prostor za registre kada se koriste za vie od
jedne namene. Nakon to se modifikuje, mogu im se vratiti originalne vrednosti.
Kada se izvrava instrukcija CALL, procesor uva povratnu adresu trenutne
subrutine na steku.
Kada se poziva subrutina, prosleuju joj se argumenti tako to se smetaju na steku.
Stek je privremeni skladini prostor za lokalne promenljive unutar subrutina.
6.2.5 Instrukcije PUSH i POP
Instrukcija PUSH
Ova instrukcija prvo dekrementira ESP, a potom kopira izvorini operand na stek.
Operand veliine 16 bita uzrokuje da se ESP dekrementira za 2. Operand veliine 32 bita
uzrokuje da se ESP dekrementira za 4. Postoje tri formata:
PUSH reg/mem16
PUSH reg/mem32
PUSH imm32
Ako program poziva procedure iz Irvine32 biblioteke, moraju se smetati 32-bitne
vrednosti na stek. Neposredne vrednosti su uvek 32-bitne u 32-bitnom reimu.
Instrukcija POP
Instrukcija POP najpre kopira element steka na koji ukazuje ESP u 16-bitni ili 32-bitni
odredini operand, a potom inkrementira ESP. Ako je operand duine 16 bita, ESP se
inkrementira za 2, a ako je 32-bitni, inkrementira se za 4. Sintaksa je:
11
POP reg/mem16
POP reg/mem32
Instrukcije PUSHFD i POPFD
Instrukcija PUSHFD smeta 32-bitni EFLAGS registar na stek, a POPFD uzima vrednost
sa steka i smeta u EFLAGS. Sintaksa je:
pushfd
popfd
Instrukcija MOV se ne moe koristiti za kopiranje flegova u promenljivu, tako da
PUSHFD moe biti najbolji nain za uvanje flegova. Nekad je korisno da se napravi kopija
flegova tako da se mogu vratiti njihove vrednosti kasnije. esto se okruuje blok koda sa
PUSHFD i POPFD:
pushfd ; sauvaj flegove
;
; neki programski iskazi...
;
popfd ; vrati flegove
Kada se koriste ove instrukcije, treba biti siguran da putanja izvrenja programa ne
preskoi POPFD instrukciju. Kada se program vremenom modifikuje, moe biti teko za
priseanje gde se nalaze sve push i pop instrukcije. Od velike je vanosti voditi preciznu
dokumentaciju.
Laki nain i manje skloniji grekama je da se sauvaju flegovi na steku, a da se odmah
potom smeste u promenljivu:
.data
saveFlags DWORD ?
.code
pushfd ; smesti flegove na stek
pop saveFlags ; kopiraj ih u promenljivu
Sledei iskazi restauriraju flegove iz iste promenljive:
push saveFlags ; smesti sauvane vrednosti na stek
popfd ; kopiraj ih u flegove
Instrukcije PUSHAD, PUSHA, POPAD i POPA
Instrukcija PUSHAD smeta sve 32-bitne registre opte namene na stek u sledeem
redosledu: EAX, ECX, EDX, EBX, ESP (vrednost pre izvravanja instrukcije PUSHAD), EBP,
ESI i EDI. Instrukcija POPAD uzima sa steka ove registre u obrnutom redosledu. Slino,
instrukcija PUSHA, uvedena sa 80286 procesorom, smeta na stek 16-bitne registre (AX, CX,
DX, BX, SP, BP, SI, DI) u navedenom redosledu. Instrukcija POPA uzima sa steka iste registre
u obrnutom redosledu.
12
Ako piete proceduru koja modifikuje veliki broj 32-bitnih registara, koristite PUSHAD
na poetku procedure, a POPAD na kraju kako biste sauvali i vratili sadraje registara. Sledei
deo koda je primer:
MySub PROC
pushad ; sauvaj registre opte namene
.
.
mov eax,...
mov edx,...
mov ecx,...
.
.
popad ; restauriraj registre opte namene
ret
MySub ENDP
Mora se istai i vaan izuzetak od gornjeg primera: procedure koje vraaju rezultat preko
jednog ili vie registara ne treba da koriste PUSHA i PUSHAD. Pretpostavimo da sledea
ReadValue procedura vraa ceo broj u EAX. Poziv instrukcije POPAD prepisuje povratnu
vrednost iz EAX:
ReadValue PROC
pushad ; sauvaj registre opte namene
.
.
mov eax,return_value
.
.
popad ; prepisuje EAX!
ret
ReadValue ENDP
Primer: Obrtanje stringa
Sledei program prolazi kroz string i smeta svaki karakter na stek. Potom uzima slova
sa steka (u obrnutom redosledu) i smeta ih nazad u istu promenljivu. Budui da je stek LIFO
struktura, slova u stringu su obrnuta.
TITLE Obrtanje stringa (RevStr.asm) INCLUDE Irvine32.inc .data aName BYTE "Abraham Lincoln",0 nameSize = ($ - aName) - 1 .code main PROC ; Smesti string na stek. mov ecx,nameSize
13
mov esi,0 L1: movzx eax,aName[esi] ; uzmi karakter push eax ; smesti na stek inc esi loop L1 ; Uzmi string sa steka, u obrnutom redosledu i smesti ga u aName. mov ecx,nameSize mov esi,0 L2: pop eax ; uzmi karakter mov aName[esi],al ; smesti u string inc esi loop L2 ; Prikai string. mov edx,OFFSET aName call WriteString call Crlf exit main ENDP END main
6.3 Definisanje i korienje procedura
U asembleru, obino se koristi termin procedura da bi se oznaila subrutina. U drugim
jezicima, subrutine se nazivaju metodama ili funkcijama.
6.3.1 Direktiva PROC
Definisanje procedure
Neformalno, moemo definisati proceduru kao imenovani blok iskaza koji se zavrava sa
povratnim iskazom (return). Procedura se deklarie koristei PROC i ENDP direktive. Mora
joj se dodeliti ime (validan indentifikator). Svaki program koji smo do sada pisali je sadrao
proceduru pod imenom main, na primer:
main PROC
.
.
main ENDP
Kada se kreira procedura razliita od poetne procedure programa, zavrava se sa
instrukcijom RET. Ova instrukcija ini da se procesor vrati na lokaciju sa koje je procedura
pozvana:
sample PROC
.
.
ret
14
sample ENDP
Poetna procedura (main) je poseban sluaj zato to se zavrava sa iskazom exit. Kada se
koristi Irvine32 biblioteka, exit je alijas za poziv ExitProcess, sistemske procedure kojom se
zavrava program:
INVOKE ExitProcess,0
6.3.2 Labele u procedurama
Podrazumevano, labele su vidljive samo unutar procedure u kojoj su deklarisane. Ovo
pravilo se esto odnosi na jump i loop instrukcije. U sledeem primeru, labela Destination mora
biti locirana u istoj proceduri kao i instrukcija JMP:
jmp Destination
Mogue je da se zaobie ovo pravilo tako to se deklarie globalna labela, koja se
identifikuje sa dvostrukim dvotakama nakon imena:
Destination::
U pogledu dizajna programa, nije dobra ideja da se skae van trenutne procedure.
Procedure imaju automatizovan nain za vraanje i podeavanje steka. Ako direktno odete van
procedure, stek se lako moe pokvariti.
Primer: sabiranje tri cela broja
Kreirajmo proceduru SumOf koja rauna zbir tri 32-bitna cela broja. Pretpostaviemo da
su celi brojevi smeteni u EAX, EBX i ECX pre poziva procedure. Procedura vraa sumu u
EAX:
SumOf PROC
add eax,ebx
add eax,ecx
ret
SumOf ENDP
6.3.3 Dokumentovanje procedura
Dobra navika je dodavanje jasne i itljive dokumentacije u program. Sledi nekoliko
sugestija za informacije koje smetate na poetak svake procedure:
Opis svih zadataka koje procedura obavlja.
Lista ulaznih parametara i njihova upotreba, oznaeni sa reima kao to je Prima.
Ako neki od ulaznih parametara imaju posebne uslove za ulazne vrednosti, izlistati
ih.
Opis vrednosti koje vraa procedura, oznaene sa reima kao to je Vraa.
15
Lista nekih posebnih zahteva, ili preduslova, koji se moraju zadovoljiti pre poziva
procedure. Mogu se oznaiti reima kao to je Zahteva. Na primer, za proceduru
koja iscrtava liniju, koristan preduslov bi bio da video adapter mora prethodno biti
u grafikom reimu.
Sa ovim idejama, dodajmo prigodnu dokumentaciju u proceduru SumOf:
;---------------------------------------------------------
Sumof PROC
;
; Rauna i vraa sumu tri 32-bitna cela broja.
; Prima: EAX, EBX, ECX, tri cela broja. Mogu biti
; oznaeni ili neoznaeni.
; Vraa: EAX = suma
;---------------------------------------------------------
add eax,ebx
add eax,ecx
ret
SumOf ENDP
6.3.4 Instrukcije CALL i RET
Instrukcija CALL poziva proceduru tako to usmerava procesor da pone izvravanje na
novoj memorijskoj lokaciji. Procedura koristi RET (return from procedure) instrukciju kako bi
vratila procesor nazad na taku u programu gde je procedura pozvana. Sa hardverske take
gledita, instrukcija CALL smeta svoju povratnu adresu na stek i kopira adresu pozvane
procedure u instrukcijski pokaziva. Kada je procedura spremna na izlazak (return), instrukcija
RET uzima povratnu adresu sa steka i smeta je u instrukcijski pokaziva. U 32-bitnom reimu,
procesor izvrava instrukcije u memoriji na koje ukazuje EIP, a u 16-bitnom reimu IP.
Primer call i return
Pretpostavimo da je u main-u CALL iskaz lociran na ofsetu 00000020. Obino, ovoj
instrukciji je potrebno pet bajtova mainskog koda, tako da sledei iskaz (MOV u ovom sluaju)
se nalazi na ofsetu 00000025:
main PROC
00000020 call MySub
00000025 mov eax,ebx
Potom, pretpostavimo da se prva izvrna instrukcija u MySub proceduri nalazi na ofsetu
00000040:
MySub PROC
00000040 mov eax,edx
.
.
ret
MySub ENDP
16
Kada se izvri instrukcija CALL (slika 7), adresa nakon nje (00000025) se smeta na stek,
a adresa od MySub se ubacuje u EIP. Sve instrukcije u MySub se izvravaju do RET instrukcije.
Kada se RET instrukcija izvri, vrednost na steku na koju ukazuje ESP se premeta u EIP (korak
1 na slici 8). U koraku 2, ESP se inkrementira kako bi ukazivao na prethodnu vrednost na steku
(korak 2).
Slika 7. Izvravanje CALL instrukcije
Slika 8. Izvravanje RET instrukcije
6.4 Pozivi ugnjedenih procedura
Pozivi ugnjedenih procedura se deavaju kada pozivana procedura poziva drugu
proceduru pre zavretka prve procedure. Pretpostavimo da main poziva proceduru Sub1. Dok
se Sub1 izvrava, poziva Sub2 proceduru. Dok se Sub2 izvrava, poziva Sub3 proceduru. Ovaj
proces je prikazan na slici 9.
17
Slika 9. Pozivi ugnjedenih procedura
Kada se izvri RET instrukcija na kraju Sub3 procedure, ona uzima vrednost sa steka na
koju ukazuje ESP i prebacuje je u EIP. Ovo prouzrokuje nastavak izvravanja sa instrukcijom
koja je bila sledea nakon poziva Sub3 instrukcije. Sledea slika pokazuje izgled steka pre
izvravanja povratka iz Sub3:
Nakon return-a, ESP ukazuje na sledeu vrednost na vrhu steka. Neposredno pre
izvravanja RET instrukcije na kraju Sub2, izgled steka je sledei:
Na kraju, kada se vraamo iz Sub1, vrednost sa vrha steka se uzima i prebacuje u
instrukcijski pokaziva i izvravanje se nastavlja u main proceduri:
18
Oigledno je da je stek pogodan za pamenje informacija, ukljuujui pozive ugnjedenih
procedura. U optem sluaju, stek se koristi kada programi moraju da prate svoje korake u
odreenom redosledu.
6.5 Prosleivanje registara kao argumenata u procedurama
Ako piete proceduru koja vri neke standardne operacije kao to su raunanje sume niza,
nije dobra ideja da se ukljuuju reference na pojedinana imena promenljivih unutar procedure.
Ako to radite, procedura bi se mogla koristiti samo sa jednim nizom. Bolji pristup je da se
prosledi ofset niza proceduri i ceo broj koji odreuje broj elemenata u nizu. To su argumenti ili
ulazni parametri procedure. U asembleru, uobiajeno je da se argumenti prosleuju preko
registara opte namene.
U prethodnom delu smo kreirali jednostavnu proceduru SumOf koja je sabirala cele
brojeve u EAX, EBX i ECX registrima. U main-u, pre poziva procedure SumOf, dodeljujemo
vrednosti registrima EAX, EBX i ECX:
.data
theSum DWORD ?
.code
main PROC
mov eax,10000h ; argument
mov ebx,20000h ; argument
mov ecx,30000h ; argument
call Sumof ; EAX = (EAX + EBX + ECX)
mov theSum,eax ; sauvaj sumu
Nakon iskaza CALL, imamo opciju da kopiramo sumu iz EAX u promenljivu.
Primer: sabiranje celobrojnog niza
Petlja za sabiranje elemenata niza se moe napisati tako da radi na najbri mogui nain,
u asembleru. Na primer, mogu se koristiti registri umesto promenljivih unutar petlje.
Kreirajmo proceduru ArraySum koja prima dva parametra iz pozivajueg programa:
pokaziva na niz 32-bitnih celih brojeva i broj elemenata u nizu. Rauna i vraa sumu
elemenata, u EAX.
;-----------------------------------------------------
ArraySum PROC
;
; Rauna sumu elemenata niza 32-bitnih celih brojeva.
19
; Prima: ESI = ofset niza
; ECX = broj elemenata u nizu
; Vraa: EAX = suma elemenata niza
;-----------------------------------------------------
push esi ; sauvaj ESI, ECX
push ecx
mov eax,0 ; postavi sumu na nulu
L1: add eax,[esi] ; dodaj svaki broj na sumu
add esi,TYPE DWORD ; ukai na sledei broj
loop L1 ; ponovi za veliinu niza
pop ecx ; restauriraj ECX, ESI
pop esi
ret ; suma je u EAX
ArraySum ENDP
Nita u ovoj proceduri nije specifino za odreeno ime ili veliinu niza. Moe se koristiti
u bilo kom programu koji treba da sabira niz 32-bitnih celih brojeva. Kadgod je mogue, trebalo
bi da kreirate procedure koje su fleksibilne i prilagodljive.
Pozivanje ArraySum
Sledi primer poziva procedure ArraySum, prosleivanja adrese od array u ESI i broja
elemenata u ECX. Nakon poziva, kopiramo sumu iz EAX u promenljivu.
.data
array DWORD 10000h,20000h,30000h,40000h,50000h
theSum DWORD ?
.code
main PROC
mov esi,OFFSET array ; ESI ukazuje na niz
mov ecx,LENGTHOF array ; ECX = velicina niza
call ArraySum ; izracunaj sumu
mov theSum,eax ; vrati je preko EAX
Na slici 11 je prikazan algoritam za proceduru ArraySum.
20
Slika 11. Algoritam za proceduru ArraySum
6.5.1 uvanje i restauracija registara
U primeru sa ArraySum, ECX i ESI su smeteni na stek na poetku procedura, a uzeti sa
steka na kraju. Ova radnja je tipina za veinu procedura koje modifikuju sadraje registara.
Uvek treba uvati i restaurirati registre koje procedura modifikuje tako da pozivajui program
moe biti siguran da nijedna od vrednosti njegovih registara nee biti prepisana. Izuzetak od
ovog pravila vai za registre koji se koriste kao povratne adrese, obino EAX. Njih ne treba
smetati i restaurirati sa steka.
6.5.2 Operator USES
Operator USES, zajedno sa direktivom PROC, omoguava izlistavanje imena registara
koji se modifikuju unutar procedure. USES govori asembleru da radi dve stvari:
Da generie instrukcije PUSH koje uvaju registre na steku na poetku procedure,
Da generie POP instrukcije koje restauriraju vrednosti registara na kraju
procedure.
21
Operator USES sledi odmah posle PROC, a praen je sa listom registara u istoj liniji koda,
odvojenih praznim mestima (space) ili tabovima (ne zarezima).
Primer upotrebe operatora USES u proceduri ArraySum:
ArraySum PROC USES esi ecx
mov eax,0 ; postavi sumu na nulu
L1:
add eax,[esi] ; dodaj svaki broj na sumu
add esi,TYPE DWORD ; pokai na sledei broj
loop L1 ; ponovi za veliinu niza
ret ; suma je u EAX
ArraySum ENDP
Odgovarajui kod koji generie asembler pokazuje efekte operatora USES:
ArraySum PROC
push esi
push ecx
mov eax,0 ; postavi sumu na nulu
L1:
add eax,[esi] ; dodaj svaki broj na sumu
add esi,TYPE DWORD ; pokai na sledei broj
loop L1 ; ponovi za veliinu niza
pop ecx
pop esi
ret ; suma je u EAX
ArraySum ENDP
Izuzetak
Postoji vaan izuzetak od ovog pravila, koji vai za procedure koje vraaju vrednosti
preko registara (obino EAX). U tom sluaju, taj registar ne treba smetati na stek. Na primer,
u proceduri SumOf u sledeem primeru, EAX se smeta i uzima sa steka, ime se gubi povratna
vrednost procedure:
SumOf PROC ; suma tri broja
push eax ; sauvaj EAX
add eax,ebx ; izraunaj sumu
add eax,ecx ; od EAX, EBX, ECX
pop eax ; suma je izgubljena!
ret
SumOf ENDP
22
6.6 Dizajniranje programa koristei procedure
Kada se kreira program, kreirajte skup specifikacija koje tano navode ta program treba
da radi. Specifikacije treba da budu rezultat paljive analize problema kojeg pokuavate da
reite. Potom dizajnirajte program na osnovu tih specifikacija. Standardni pristup dizajnu je
podela celokupnog problema na pojedinane zadatke, proces poznat kao funkcionalna
dekompozicija, ili top-down dizajn. Zasniva se na nekim osnovnim principima:
Veliki problem se moe lake podeliti na manje zadatke.
Program se lake odrava ako se svaka procedura posebno testira.
Top-down dizajn vam omoguava da vidite kako su procedure meusobno
povezane.
Kada ste sigurni u celokupni dizajn, moete lake da se koncentriete na detalje,
pisajui kod koji implementira svaku proceduru.
6.6.1 Top-down pristup dizajnu programa za sabiranje lanova niza
Zadatak je da se napie program koji pita korisnika da unese tri 32-bitna cela broja, smeta
ih u niz, rauna sumu niza i prikazuje je na ekranu.
Sledei pseudokod pokazuje kako moemo podeliti specifikacije na zadatke:
Program za sabiranje lanova niza
Pitaj korisnika za unos tri cela broja
Izraunaj sumu elemenata niza
Prikai sumu
Kao priprema za pisanje programa, dodelimo imena procedurama za svaki zadatak:
Main
PromptForIntegers
ArraySum
DisplaySum
U asembleru, ulazno-izlazne operacije esto zahtevaju veoma detaljan kod. Kako bismo
smanjili nivo detalja, moemo pozvati procedure koje briu sadraj ekrana, prikazuju string,
unose ceo broj i prikazuju ga:
Main
Clrscr ; obrii sadraj ekrana
PromptForIntegers
WriteString ; prikai string
ReadInt ; unesi ceo broj
ArraySum ; saberi elemente niza
DisplaySum
WriteString ; prikai string
WriteInt ; prikai ceo broj
23
Na slici 12 je prikazan strukturni dijagram koji opisuje strukturu programa. Procedure iz
linkerske biblioteke su obojene sivo.
Slika 12. Strukturni dijagram programa za sabiranje elemenata niza
Kreirajmo minimalnu verziju programa koja se naziva zaglavlje programa. Sadri samo
prazne ili skoro prazne procedure. Program se asemblira i pokree, ali ne radi nita korisno.
TITLE Program za sabiranje celih brojeva (Sum1.asm)
; Ovaj program pita korisnika za unos tri cela broja,
; smeta ih u niz, rauna sumu niza i prikazuje je.
INCLUDE Irvine32.inc
.code
main PROC
; Kontrolna procedura glavnog programa.
; Poziva: Clrscr, PromptForIntegers,
; ArraySum, DisplaySum
Exit
main ENDP
;-----------------------------------------------------
PromptForIntegers PROC
;
; Pita korisnika za unos tri cela broja, smeta ih u niz
; Prima: ESI ukazuje na niz dvostrukih rei,
; ECX = veliina niza.
; Vraa: nita
; Poziva: ReadInt, WriteString
;-----------------------------------------------------
ret
PromptForIntegers ENDP
24
;-----------------------------------------------------
ArraySum PROC
;
; Rauna sumu niza 32-bitnih celih brojeva.
; Prima: ESI ukazuje na niz, ECX = veliina niza
; Vraa: EAX = suma elemenata niza
;-----------------------------------------------------
ret
ArraySum ENDP
;-----------------------------------------------------
DisplaySum PROC
;
; Prikazuje sumu na ekranu.
; Prima: EAX = suma
; Vraa: nita
; Poziva: WriteString, WriteInt
;-----------------------------------------------------
ret
DisplaySum ENDP
END main
Zaglavlje programa daje vam mogunost za mapiranje svih poziva procedura,
prouavanje zavisnosti izmeu procedura i mogue unapreenje strukturnog dizajna pre
kodiranja. Koristite komentare u svakoj proceduri kako biste objasnili njenu namenu i zahteve
za parametrima.
Implementacija programa za sabiranje elemenata niza
Deklarisaemo niz od tri cela broja i koristiti definisanu konstantu za veliinu niza u
sluaju da je kasnije elimo promeniti:
INTEGER_COUNT = 3
array DWORD INTEGER_COUNT DUP(?)
Dva stringa se koriste kao pitanja na ekranu:
str1 BYTE "Uneti oznaceni ceo broj: ",0
str2 BYTE "Suma celih brojeva je: ",0
Procedura main brie sadraj ekrana, prosleuje pokaziva na niz proceduri
PromptForIntegers, poziva ArraySum i DisplaySum:
call Clrscr
mov esi,OFFSET array
mov ecx,INTEGER_COUNT
call PromptForIntegers
call ArraySum
call DisplaySum
25
PromptForIntegers poziva WriteString kako bi korisnik znao da treba da unese broj.
Potom poziva ReadInt za unos broja od korisnika i smeta ga u niz na koji ukazuje ESI. Petlja
izvrava ove korake nekoliko puta.
ArraySum rauna i vraa sumu elemenata niza.
DisplaySum prikazuje poruku na ekranu i poziva WriteInt za prikaz broja koji se nalazi u
EAX.
Sledi i kompletan program.
TITLE Integer Summation Program (Sum2.asm) ; Ovaj program pita korisnika za unos tri cela broja, ; smeta ih u niz, rauna i prikazuje sumu niza. INCLUDE Irvine32.inc INTEGER_COUNT = 3 .data str1 BYTE "Uneti oznaceni ceo broj: ",0 str2 BYTE "Suma celih brojeva je: ",0 array DWORD INTEGER_COUNT DUP(?) .code main PROC call Clrscr mov esi,OFFSET array mov ecx,INTEGER_COUNT call PromptForIntegers call ArraySum call DisplaySum exit main ENDP ;----------------------------------------------------- PromptForIntegers PROC USES ecx edx esi ; ; Pita korisnika za unos proizvoljnog broja celih brojeva ; i ubacuje ih u niz. ; Prima: ESI ukazuje na niz, ECX = veliina niza ; Vraa: nita ;----------------------------------------------------- mov edx,OFFSET str1 ; "Uneti oznaceni ceo broj" L1: call WriteString ; prikai string call ReadInt ; uneti broj u EAX call Crlf ; prei na sledeu liniju na ekranu mov [esi],eax ; smesti u niz add esi,TYPE DWORD ; sledei broj loop L1 ret PromptForIntegers ENDP
26
;----------------------------------------------------- ArraySum PROC USES esi ecx ; ; Rauna sumu niza 32-bitnih celih brojeva. ; Prima: ESI ukazuje na niz, ECX = broj ; elemenata niza. ; Vraa: EAX = suma elemenata niza ;----------------------------------------------------- mov eax,0 ; postavi sumu na nulu L1: add eax,[esi] ; dodaj svaki broj na sumu add esi,TYPE DWORD ; ukai na sledei broj loop L1 ; ponovi za veliinu niza ret ; suma je u EAX ArraySum ENDP ;----------------------------------------------------- DisplaySum PROC USES edx ; ; Prikazuje sumu na ekranu. ; Prima: EAX = suma ; Vraa: nita ;----------------------------------------------------- mov edx,OFFSET str2 ; "Suma celih brojeva je:" call WriteString call WriteInt ; prikai EAX call Crlf ret DisplaySum ENDP END main