From eba5e42982cada0a45a344dde01f3431b1dbf442 Mon Sep 17 00:00:00 2001 From: Dan Novischi Date: Mon, 27 Apr 2026 11:23:03 +0300 Subject: [PATCH] Add factorial iterative demo for calling convention Add an iterative factorial example demonstrating calling convention for both x86 32-bit and x86_64. Signed-off-by: Dan Novischi --- curs/chap-07-functii/22-bonus/Makefile | 19 +++++ curs/chap-07-functii/22-bonus/README.md | 37 +++++++++ curs/chap-07-functii/22-bonus/factorial.asm | 83 +++++++++++++++++++ curs/chap-07-functii/22-bonus/factorial32.asm | 81 ++++++++++++++++++ 4 files changed, 220 insertions(+) create mode 100644 curs/chap-07-functii/22-bonus/Makefile create mode 100644 curs/chap-07-functii/22-bonus/README.md create mode 100644 curs/chap-07-functii/22-bonus/factorial.asm create mode 100644 curs/chap-07-functii/22-bonus/factorial32.asm diff --git a/curs/chap-07-functii/22-bonus/Makefile b/curs/chap-07-functii/22-bonus/Makefile new file mode 100644 index 00000000..44d4f4f8 --- /dev/null +++ b/curs/chap-07-functii/22-bonus/Makefile @@ -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 diff --git a/curs/chap-07-functii/22-bonus/README.md b/curs/chap-07-functii/22-bonus/README.md new file mode 100644 index 00000000..f0c9ffa4 --- /dev/null +++ b/curs/chap-07-functii/22-bonus/README.md @@ -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. diff --git a/curs/chap-07-functii/22-bonus/factorial.asm b/curs/chap-07-functii/22-bonus/factorial.asm new file mode 100644 index 00000000..74604811 --- /dev/null +++ b/curs/chap-07-functii/22-bonus/factorial.asm @@ -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 + diff --git a/curs/chap-07-functii/22-bonus/factorial32.asm b/curs/chap-07-functii/22-bonus/factorial32.asm new file mode 100644 index 00000000..4e45ed9e --- /dev/null +++ b/curs/chap-07-functii/22-bonus/factorial32.asm @@ -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