/* Poczatek kodu w C - plik suma.c */

#include <stdio.h>

int suma1(int, int); /* Prototypy funkcji, ktore       */
int suma2(int, int); /* zostana napisane w asemblerze. */

int main() {
  int s;
  
  s = suma1(7, 9);
  /* Powyzsze wywolanie wyglada w asemblerze tak:
        push 9               ; odlozenie na stos drugiego argumentu
        push 7               ; odlozenie na stos pierwszego argumentu
        call suma1           ; wolanie funkcji asemblerowej
        add  esp, 8          ; wyczyszczenie stosu z argumentow
        mov  [ebp - 4], eax  ; zapisanie wyniku zwroconego w rejestrze eax
  */
  printf("suma1 = %d\n", s);

  s = suma2(7, 9);
  printf("suma2 = %d\n", s);

  return 0;
}

/* Koniec kodu w C */

; Poczatek kodu w asemblerze - plik suma.asm

global suma1 ; Etykiety poczatkow funkcji trzeba zadeklarowac jako global,
global suma2 ; zeby byly widoczne na zewnarz modulu asemblerowego.

section .text

; Funkcje suma1, niezbyt optymalnie, mozna w C napisac tak:
; int suma1(int a, int b) {
;   int c;
;
;   c = a + b;
;   return c;
; }
; GCC z wylaczona optymalizacja przetlumaczy to mniej wiecej tak (9 instrukcji):
suma1:
        ; Prolog funkcji
        push ebp            ;  Zapamietaj na stosie wskaznik poprzedniej ramki stosu.
        mov ebp, esp        ;  Zapisz do ebp wskaznik biezacej ramki stosu.
        sub esp, 4          ;* Zarezerwuj 4 bajty na stosie na zmienna c.
        ; Cialo funkcji
        mov eax, [ebp + 12] ;  Przepisz do eax argument b.
        add eax, [ebp + 8]  ;  Dodaj do eax argument a;
        mov [ebp - 4], eax  ;* Zapisz wynik w zmiennej c;
        mov eax, [ebp - 4]  ;* Zwroc wartosc ze zmiennej c.
        ; Epilog funkcji
        leave               ;  To jest rownowazne wykonaniu sekwencji:
                            ;    mov esp, ebp ; Wyczysc stos ze zmiennych lokalnych.
                            ;    pop ebp      ; Przywroc poprzedni wskaznik ramki stosu.
        ret                 ;
; Wlaczenie optymalizacji (opcja -O3) usunie instrukcje oznaczone gwiazdka,
; czyli usunie zmienna c, ktora oczywiscie nie jest potrzebna (pozostalo 6 instrukcji).
; Mozna jednak zaimplementowac te funkcje sprytniej tak (3 instrukcje):
suma2:
        mov eax, [esp + 8] ; Przepisz do eax drugi argument.
        add eax, [esp + 4] ; Dodaj do niego pierwszy argument.
        ret                ; W eax jest to, co trzeba zwrocic.
; Faktycznie GCC tez wie, ze tak mozna, gdy mu dodatkowo wlaczyc opcje
; -fomit-frame-pointer ale nie z kazda funkcja tak dobrze sobie poradzi.

; Koniec kodu w asemblerze

# Skompilowac powyzsze mozna tak:
	nasm -f elf -o suma.o suma.asm
	gcc -o suma suma.c suma.o