summaryrefslogtreecommitdiff
path: root/gcd-amd64.S
diff options
context:
space:
mode:
Diffstat (limited to 'gcd-amd64.S')
-rw-r--r--gcd-amd64.S161
1 files changed, 161 insertions, 0 deletions
diff --git a/gcd-amd64.S b/gcd-amd64.S
new file mode 100644
index 0000000..16be38f
--- /dev/null
+++ b/gcd-amd64.S
@@ -0,0 +1,161 @@
+# This program is for amd64 arch (64 bits) running Linux, Solaris or FreeBSD.
+# Written for GNU Assembler (as) with AT&T syntax
+#
+# Synopsis:
+#
+# $ gcc -nostdlib [-D OS=LINUX] gcd-amd64.S -o gcd-amd64-linux
+# or
+# $ cpp [-D OS=LINUX] gcd-amd64.S | as -o gcd-amd64-linux.o \
+# && ld gcd-amd64-linux.o -o gcd-amd64-linux
+#
+#
+# $ gcc -nostdlib -D OS=SOLARIS gcd-amd64.S -o gcd-amd64-solaris
+# or
+# $ cpp -D OS=SOLARIS gcd-amd64.S | gas -64 -o gcd-amd64-solaris.o \
+# && gld -m elf_x86_64_sol2 gcd-amd64-solaris.o -o gcd-amd64-solaris
+#
+# $ clang -nostdlib -D OS=FREEBSD gcd-amd64.S -o gcd-amd64-freebsd
+#
+
+#define LINUX 1
+#define SOLARIS 2
+#define FREEBSD 3
+
+#ifndef OS
+#define OS LINUX
+#endif
+
+
+.data
+
+# Buffer for output:
+buffer:
+ .space 64 # enough for a 64-bit integer
+buf_end:
+ .byte 10 # new line
+
+
+.text
+.globl _start
+
+
+# GCD of two numbers.
+# input: rax, rbx - two numbers
+# output: rax - GCD
+# uses: rax, rbx, rdx
+gcd2:
+ and %rbx, %rbx # is %rbx == 0 ?
+ jz gcd2_exit # %rbx == 0, go to exit and return %rax (GCD)
+ xor %rdx, %rdx # set %rdx = 0
+ div %rbx # divide: %rdx:%rax / %rbx, actually: %rax / %rbx
+ mov %rbx, %rax # drop quotient in %rax and keep prrvious %rbx in %rax
+ mov %rdx, %rbx # put remainder in %rbx
+ jmp gcd2
+gcd2_exit:
+ ret
+
+
+
+# Print an unsigned integer in rax.
+# input: rax - unsigned integer to print
+# uses: rax, rbx, rcx, rdx, rdi, buffer
+print:
+ mov $10, %rcx # set %rcx = 10 (radix)
+ mov $buf_end, %rsi
+
+next_digit:
+ xor %rdx, %rdx # set %rdx = 0
+ div %rcx # divide: %rdx:%rax / %rcx, actually: %rax / %rcx
+ # %rdx is a remainder (0-9), it fits into %dl
+ add $48, %dl # get ASCII code: 0 => 48 = '0', 1 => 49 = '1'
+ dec %rsi # put remainders going from the end of the buffer
+ mov %dl, (%rsi)
+ # now rax is a quotient
+ and %rax, %rax # is quotient == 0 ?
+ jnz next_digit # quotient is not 0, go on
+
+ # printing the number:
+#if OS == LINUX
+ mov $1, %rax # syscall `write'
+#elif OS == SOLARIS
+ mov $4, %rax # syscall `write'
+#elif OS == FREEBSD
+ mov $4, %rax # syscall `write'
+#else
+#error bad OS.
+#endif
+ mov $1, %rdi # write to stdout
+ mov $buf_end, %rdx
+ sub %rsi, %rdx # rdx is a number of characters to write (buf_end - rsi)
+ inc %rdx # + new line
+ syscall # do syscall (print the number at rsi)
+ ret
+
+
+# Convert string into unsigned integer
+# input: rsi - pointer to string
+# output: rbx - unsigned integer
+# uses: rax, rbx, rcx, rdi, direction flag
+str2uint:
+ xor %rcx, %rcx # it will be the string length
+ dec %rcx # rcx = -1 != 0 for repne
+ xor %rax, %rax # search for 0 (%rax = %al = 0)
+ mov %rsi, %rdi
+ cld # Search forward (std - backward)
+ repne scasb # search for 0 (in %al), incrementing rdi, decrementing rcx
+ not %rcx # invert rcx to have the string length
+ dec %rcx # minus ending zero
+
+ xor %rbx, %rbx
+str2uint_loop:
+ lodsb # going forward from rsi
+ # HINT: assert '0' <= al <= '9'
+ lea (%rbx, %rbx, 4), %rbx # rbx = 4*rbx + rbx = 5*rbx ;-)
+ lea -48(%rax, %rbx, 2), %rbx # rbx = 2*rbx + %rax - 48
+ # rbx is multiplied by 10 each iteration,
+ # rax-48 will be multiplied at the next iteration ;-)
+ loop str2uint_loop
+ ret
+
+
+
+# Entry point for the program
+_start:
+
+ # Access command line.
+ # Example: ./gcd-amd64-linux 11 22 33
+#if OS == FREEBSD
+ mov %rdi, %rsp
+#endif
+ pop %rcx # Get the number of command-line options (4)
+ pop %rsi # Get the pointer to the program name (./gcd-amd64-linux),
+ dec %rcx # minus program name
+ jz exit # no arguments are given - exiting
+
+ xor %rax, %rax
+gcd_loop:
+ pop %rsi # get next command line argument
+ mov %rcx, %r8 # save argument counter
+ mov %rax, %r9 # save current GCD
+ call str2uint # convert string at rsi to integer at rbx
+ mov %r9, %rax # restore current GCD
+ call gcd2 # gcd of rax and rbx (returned by str2uint)
+ # now rax is a GCD
+ mov %r8, %rcx # restore argument counter
+ loop gcd_loop
+
+ call print # print rax with GCD
+
+exit:
+#if OS == LINUX
+ mov $60, %rax # exit syscall
+#elif OS == SOLARIS
+ mov $1, %rax # exit syscall
+#elif OS == FREEBSD
+ mov $1, %rax # exit syscall
+#else
+#error bad OS.
+#endif
+ xor %rdi, %rdi # exit code = 0
+ syscall
+