Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions curs/chap-07-functii/22-bonus/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
NASM=nasm
GCC=gcc

all: factorial factorial32

factorial: factorial.o
$(GCC) factorial.o -no-pie -o factorial

factorial.o: factorial.asm
$(NASM) -felf64 factorial.asm -o factorial.o

factorial32: factorial32.o
$(GCC) -m32 factorial32.o -no-pie -o factorial32

factorial32.o: factorial32.asm
$(NASM) -felf32 factorial32.asm -o factorial32.o

clean:
rm -f factorial.o factorial factorial32.o factorial32
37 changes: 37 additions & 0 deletions curs/chap-07-functii/22-bonus/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Iterative factorial (NASM x86-64)

This small example implements an iterative factorial routine in NASM targeting x86-64 (System V AMD64). It demonstrates a simple RBP-based frame, calling the function from `main`, and using `scanf`/`printf` to read and write values.

Files:
- `factorial.asm` — the NASM source
- `Makefile` — assemble and link

Build and run
```
make
printf "5\n" | ./factorial
```

Calling convention notes (System V AMD64 / Linux):

- Parameter passing registers (in order):
1) RDI
2) RSI
3) RDX
4) RCX
5) R8
6) R9

- Return value: RAX (and RDX:RAX for 128-bit integer results in some cases)

- Caller-saved (volatile) registers — caller must save these if it needs them preserved across a call:
- RAX, RCX, RDX, RSI, RDI, R8, R9, R10, R11

- Callee-saved (non-volatile) registers — callee must preserve these (save/restore if it uses them):
- RBX, RBP, R12, R13, R14, R15

This example uses an RBP frame for clarity. The factorial function saves RBP at entry and restores it before returning. It does not use other callee-saved registers, so none of those are pushed.

Edge cases and notes:
- The code treats inputs <= 1 as factorial == 1.
- No overflow checks are performed — factorial grows very fast and will overflow a 64-bit value for relatively small n.
83 changes: 83 additions & 0 deletions curs/chap-07-functii/22-bonus/factorial.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
; Iterative factorial example in NASM for x86_64 (System V AMD64)
; Uses a RBP frame in the factorial function (and in main).
; Reads an int via scanf, calls factorial(n), prints unsigned long long via printf.

section .data
fmt_prompt: db "n = ", 0
fmt_scan: db "%d", 0
fmt_print: db "%llu", 10, 0 ; "%llu\n\0"

section .text
global main
extern scanf
extern printf

; unsigned long long factorial(unsigned long long n)
; iterative implementation using RBP frame.
; Input: n in RDI. Return: result in RAX.

factorial:
push rbp
mov rbp, rsp
; No other callee-saved registers used, so no further pushes necessary.

mov rcx, rdi ; counter = n
cmp rcx, 1
jbe .base_case

mov rax, 1 ; result = 1
.loop:
mul rcx ; unsigned multiply: RDX:RAX = RAX * RCX -> result in RAX (low 64 bits)
dec rcx
cmp rcx, 1
ja .loop
jmp .done
.base_case:
mov rax, 1
.done:
mov rsp, rbp
pop rbp
ret


; main:
; - read int n via scanf("%d", &n)
; - call factorial(n)
; - print result via printf("%llu\n", result)

main:
push rbp
mov rbp, rsp
; allocate 16 bytes for local space (4-byte int + padding, maintains 16-byte alignment)
sub rsp, 16

; printf("n = ")
lea rdi, [rel fmt_prompt]
; xor eax, eax
call printf

; call scanf(fmt_scan, &n)
lea rsi, [rbp-4] ; &n (scanf second arg)
lea rdi, [rel fmt_scan] ; fmt string in RDI
xor eax, eax ; AL=0 for varargs (no SSE params used)
call scanf

; move n (32-bit) into RDI (argument for factorial)
mov eax, dword [rbp-4]
mov edi, eax ; move into 32-bit edi (zero-extends to 64-bit RDI)

call factorial

; printf("%llu\n", result)
mov rsi, rax ; result -> second arg
lea rdi, [rel fmt_print] ; fmt -> first arg
xor eax, eax ; AL=0 for varargs
call printf

; return 0
mov eax, 0
; mov rsp, rbp
; pop rbp
leave
ret

81 changes: 81 additions & 0 deletions curs/chap-07-functii/22-bonus/factorial32.asm
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
; Iterative factorial example in NASM for x86 32-bit (cdecl calling convention)
; Uses an EBP frame in the factorial function (and in main).
; Reads an int via scanf, calls factorial(n), prints unsigned int via printf.

section .data
fmt_prompt: db "n = ", 0
fmt_scan: db "%d", 0
fmt_print: db "%u", 10, 0 ; "%u\n\0"

section .text
global main
extern scanf
extern printf

; unsigned int factorial(unsigned int n)
; iterative implementation using EBP frame.
; Input: n on stack at [ebp+8]. Return: result in EAX.

factorial:
push ebp
mov ebp, esp
; No other callee-saved registers used, so no further pushes necessary.

mov ecx, [ebp+8] ; ecx = n (first argument on stack)
cmp ecx, 1
jbe .base_case

mov eax, 1 ; result = 1
.loop:
mul ecx ; unsigned multiply: EDX:EAX = EAX * ECX -> result in EAX (low 32 bits)
dec ecx
cmp ecx, 1
ja .loop
jmp .done
.base_case:
mov eax, 1
.done:
pop ebp
ret


; main:
; - read int n via scanf("%d", &n)
; - call factorial(n)
; - print result via printf("%u\n", result)

main:
push ebp
mov ebp, esp
; allocate 4 bytes for local variable n
sub esp, 4

; printf("n = ")
push fmt_prompt
call printf
add esp, 4 ; clean up stack (1 arg * 4 bytes)

; call scanf(fmt_scan, &n)
lea eax, [ebp-4] ; &n
push eax ; second arg: &n
push fmt_scan ; first arg: format string
call scanf
add esp, 8 ; clean up stack (2 args * 4 bytes)

; call factorial(n)
mov eax, [ebp-4] ; load n
push eax ; push argument
call factorial
add esp, 4 ; clean up stack (1 arg * 4 bytes)

; printf("%u\n", result)
push eax ; second arg: result (still in EAX)
push fmt_print ; first arg: format string
call printf
add esp, 8 ; clean up stack (2 args * 4 bytes)

; return 0
mov eax, 0
mov esp, ebp
pop ebp
ret