From ae62679d7664e0ecb28efd5381d6f619ed31437e Mon Sep 17 00:00:00 2001 From: Fabio Scotto di Santolo Date: Thu, 3 Jul 2025 14:47:48 +0200 Subject: [PATCH] Added a basic calculator in assembly x86 --- asmc32.sh | 6 +- calc.asm | 233 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 236 insertions(+), 3 deletions(-) create mode 100644 calc.asm diff --git a/asmc32.sh b/asmc32.sh index 87a0353..d0f8fff 100755 --- a/asmc32.sh +++ b/asmc32.sh @@ -4,9 +4,9 @@ compile() { local filename="$(basename "$1")" local basename="${filename%.*}" - nasm -f elf32 -g -F dwarf -o "$basename.o" "$filename" - ld -m elf_i386 -o "$basename" "$basename.o" - rm "$basename.o" + nasm -f elf32 -g -F dwarf -o "$basename.o" "$filename" && \ + ld -m elf_i386 -o "$basename" "$basename.o" && \ + rm "$basename.o" } compile "$@" diff --git a/calc.asm b/calc.asm new file mode 100644 index 0000000..ebd49e0 --- /dev/null +++ b/calc.asm @@ -0,0 +1,233 @@ +; A simple calculator program that do simple operation (+, -, *, /) +; +; Author: Fabio Scotto di Santolo +; Date: 03/07/2025 + +global _start + +section .text +_start: + ; Prompt for first number: + mov eax, 0x04 ; write syscall + mov ebx, 1 ; STDOUT file descriptor + mov ecx, nprompt + mov edx, len1 + int 0x80 ; trigger write syscall + mov eax, 0x03 ; read syscall + mov ebx, 0 ; STDIN file descriptor + mov ecx, buffer1 + mov edx, 32 + int 0x80 ; trigger read syscall + mov edi, buffer1 ; save buffer address + mov esi, eax ; save length + + ; Prompt for second number: + mov eax, 0x04 ; write syscall + mov ebx, 1 ; STDOUT file descriptor + mov ecx, nprompt + mov edx, len1 + int 0x80 ; trigger write syscall + mov eax, 0x03 ; read syscall + mov ebx, 0 ; STDIN file descriptor + mov ecx, buffer2 + mov edx, 32 + int 0x80 ; trigger read syscall + + ; Convert second number + mov ecx, buffer2 ; save buffer address + mov ebx, eax ; save length + call ascii_to_int + push eax ; save converted second number in the stack + + ; Convert first number + mov ecx, edi ; ECX = address + mov ebx, esi ; EBX = length + call ascii_to_int ; EAX = first number + push eax ; save converted first number in the stack + + ; Prompt for operand: + mov eax, 0x04 ; write syscall + mov ebx, 1 ; STDOUT file descriptor + mov ecx, oprompt + mov edx, len2 + int 0x80 ; trigger write syscall + mov eax, 0x03 ; read syscall + mov ebx, 0 ; STDIN file descriptor + mov ecx, buffer3 + mov edx, 1 + int 0x80 ; trigger read syscall + + ; Check operand + mov eax, [buffer3] ; move in EAX operand + cmp eax, 0x2B ; compare with + + je .add + cmp eax, 0x2D ; compare with - + je .sub + cmp eax, 0x2A ; compare with * + je .mul + cmp eax, 0x2F ; compare with / + je .div + jmp end ; safety jmp to end + +.add: + pop eax + pop ebx + add eax, ebx + jmp result + +.sub: + pop eax + pop ebx + sub eax, ebx + jmp result + +.mul: + pop eax + pop ebx + imul eax, ebx + jmp result + +.div: + pop eax + pop ebx + xor edx, edx + idiv ebx + jmp result + +result: + ; Print operation result + mov ebx, eax + mov eax, msg + call concat_string_num ; concat result with final message + + mov eax, 0x04 ; write syscall + mov ebx, 1 ; STDOUT file descriptor + int 0x80 ; trigger syscall + jmp end + +end: + ; call syscall exit + mov eax, 0x1 + xor ebx, ebx ; reset register to 0 + int 0x80 ; trigger syscall + +; --- ASCII to Integer Conversion Routine --- +ascii_to_int: + push ebx + push ecx + push edx + push esi + + xor eax, eax + xor edx, edx + mov esi, ecx + mov ecx, ebx + +convert_loop: + cmp byte [esi], 0xA ; newline? + je convert_done + cmp byte [esi], 0xD ; carriage return? + je convert_done + + mov ebx, 10 + mul ebx ; EAX *= 10 + + movzx ebx, byte [esi] + sub ebx, '0' + add eax, ebx ; EAX += digit + + inc esi + loop convert_loop + +convert_done: + pop esi + pop edx + pop ecx + pop ebx + ret + +; ---------------------------------------------------------------- +; concat_string_num +; Concatenates a null-terminated string in EAX with a number in EBX. +; Result: +; ECX = pointer to result string (concat_buf) +; EDX = length of result string (excluding null terminator) +; Clobbers: +; EAX, EBX, ESI, EDI, EDX, AL, ECX +; ---------------------------------------------------------------- +concat_string_num: + push eax + push ebx + push esi + push edi + + ; Copy string from [EAX] to concat_buf + mov esi, eax + mov edi, concat_buf +.copy_str: + lodsb + stosb + test al, al + jnz .copy_str + dec edi ; go back to overwrite null terminator + + ; Convert EBX (number) to ASCII in num_buf (reversed order) + mov eax, ebx ; number to convert + mov esi, num_buf + 11 ; write from the end + mov byte [esi], 0 ; null terminator +.convert_digit: + dec esi + xor edx, edx + mov ebx, 10 + div ebx ; EAX = EAX / 10, EDX = remainder + add dl, '0' + mov [esi], dl + test eax, eax + jnz .convert_digit + + ; Copy number string from [ESI] to buffer (at current EDI) + mov ecx, num_buf + 12 + sub ecx, esi ; ECX = number string length + rep movsb + + ; Add newline at end + mov byte [edi], 0xA + inc edi + + ; Null terminator + mov byte [edi], 0 + + ; Output: ECX = buffer, EDX = length + mov ecx, concat_buf + mov edi, concat_buf + xor edx, edx +.calc_len: + cmp byte [edi + edx], 0 + je .done + inc edx + jmp .calc_len + +.done: + pop edi + pop esi + pop ebx + pop eax + ret + +section .data + nprompt: db "insert a number: " + len1: equ $ - nprompt + + oprompt: db "insert a operand (+, -, * or /): " + len2: equ $ - oprompt + + msg: db "Result is ", 0 + msglen: equ $ - msg + +section .bss + buffer1: resb 32 + buffer2: resb 32 + buffer3: resb 1 + concat_buf: resb 256 + num_buf: resb 12 +