-
Notifications
You must be signed in to change notification settings - Fork 0
/
gcd-amd64.S
161 lines (136 loc) · 4.33 KB
/
gcd-amd64.S
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
# 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