Laborator 11 - Exemple

Programare multi-modul (asm+asm)

Exemple

Se va scrie un program care calculeaza factorialul unui numar. "Programul principal" este main.asm, iar funcția factorial este definită în modul.asm. Pentru a putea asambla și edita legaturi este nevoie de linia de comandă. Mai jos este prezentat procesul de asamblare, editare de legături ce duce la un program executabil:

  • Fie directorul asc pe discul D, în acest director se găsesc sursele programului (main.asm și modul.asm);
  • Este necesar ca în directorul asc să se copieze și nasm.exe și alink.exe (programe disponibile în directorul nasm din setul de instrumente);
  • Pentru a usura navigarea în linia de comandă către locația pe disc unde se află sursele se poate folosi programul Total Commander, se navighează vizual până în directorul asc și se lansează linia de comandă (a se vedea figura de mai jos)

  • dacă nu se vrea utilizarea programului Total Commander, se lansează cmd.exe și cu urmatoarea secvență de comenzi se poate naviga în directorul asc
> d:
> cd  asc
  • indiferent de varianta aleasă rezultatul trebuie să fie cel din figura de mai jos

  • comanda dir listează conținutul directorului curent, se verifică dacă directorul în care s-a navigat conține sursele .asm și programele alink.exe și nasm.exe (a se vedea figura de mai jos)

  • se asamblează cele două surse folosind secvența de comenzi
> nasm -fobj modul.asm
> nasm -fobj main.asm
  • rezultatul este cel din figura de mai jos, două fișiere .obj

  • folosind alink.exe (editor de legături), din cele două fișiere .obj se creează programul executabil, numele fișierului .exe rezultat este identic cu primul fișier .obj dat ca și parametru editorului de legături, în acest caz main. Programul poate fi rulat, în acest caz main.exe afișează pe ecran 6! = 720 (a se vedea figura de mai jos)

  • pentru a depana, codul din ollydbg, File->Open si se deschide fisierul executabil, în acest caz main.exe

Programe sursa

Exemplul 1:
  1. lab11_1.asm
Exemplul 2:
  1. lab11_proc_main.asm
  2. factorial.asm
Exemplul 3:
  1. main.asm
  2. modul.asm

Factorial recursiv - Exemplu propus de studentul Molnar Radu, grupa 215, 2017-2018

bits 32 ; assembling for the 32 bits architecture
; declare the EntryPoint (a label defining the very first instruction of the program)
global start        

; declare external functions needed by our program
extern exit,printf,scanf; adaugam functile externe de care avem nevoie
import exit msvcrt.dll    
import printf msvcrt.dll
import scanf msvcrt.dll

; our data is declared here (the variables needed by our program)
segment data use32 class=data
    ; ...
    text db "introduceti un n=",0
    final db "n!=%d",0
    format db "%d",0
    a resd 1                  ; variabila a va contine numarul n citit de la tastatura
    

; our code starts here
segment code use32 class=code
    factor:
        ;pentru a implementa problema recursiv trebuie sa o despartim in cazuri
        ;la calcularea factorialului recursiv exista doua cazuri:
        ;n!=n*(n-1)!       - iteratia curenta
        ;0!=1                  - conditia de oprire
        ;subprogramul se va reapela pana in momentul in care se ajunge la ecx=0 moment in care se atribuie eax = 1 si ne intoarcem la pasul anterior
        mov ecx, [esp+4] ;mutam in ecx numarul de pasi pe care ii mai avem de facut
        jecxz sf ;daca ecx ajunge sa fie 0 sarim la eticheta sf pentru a putea incepe sa calculam factorialul
        ;daca am trecut de compararea cu 0 atunci ajungem la primul caz al recursivitatii
        ;formula lui fiind n!=n*(n-1)!
        dec ecx; decrementam ecx pentru a putea sa reapelam functia pentru pasul urmator
        push ecx;  depunem pe stiva valoarea n curenta de calcul a factorialului 
        call factor; apelam functia cu parametrul curent valoarea de pe stiva
        mul dword [esp+8]; inmultim cu valoarea coresp. pasului actual
        add esp,4; eliberam stiva de parametrul utilizat pentru a ajunge la adresa de revenire a pasului anterior
        jmp return; salt la eticheta return pentru a putea invoca revenirea din subprogram
        
   sf:
        mov eax,1;cum recursivitatea noastra are doua cazuri am ajuns in cazul in care ecx e 0 si returnam 1 – conditia de oprire 
        ;0!=1
        return:
        ret ;ne intoarcem la pasul anterior sau in programul principal
   start:
        ; ...
        ;tiparirea mesajului
        push dword text
        call [printf]
        add esp,4

        ;citirea lui n de la tastatura
        push dword a
        push dword format
        call [scanf]
        add esp,4*2 
        
        mov ecx,0
        mov eax,0;pregatim registrii pentru apelare
        push dword [a] ;salvam pe stiva numarul n
        call factor ;apelam functia

        ;afisarea rezultatului
        push eax
        push final
        call [printf]
        ; exit(0)
        push dword 0; push the parameter for exit onto the stack
        call [exit]; call exit to terminate the program