Vezba 6 - Procedure

Embed Size (px)

DESCRIPTION

Vezba 6 - Procedure

Citation preview

  • 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