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
|
# This program is for Linux on Intel x86 arch (32 bits).
# Written for GNU Assembler (as) with AT&T syntax
# To make an executable binary:
# gcc -nostdlib gcd-x86-linux.s -o gcd-x86-linux
# or
# as gcd-x86-linux.s -o gcd-x86-linux.o && \
# ld gcd-x86-linux.o -o gcd-x86-linux
# On 64 bits system:
# gcc -m32 -nostdlib gcd-x86-linux.s -o gcd-x86-linux
# or
# as --32 gcd-x86-linux.s -o gcd-x86-linux.o && \
# ld -melf_i386 gcd-x86-linux.o -o gcd-x86-linux
#
# To run:
# ./gcd-x86-linux 11 22 33 121 792
# (output should be 11)
.data
# Buffer for output:
buffer:
.space 32 # enough for 32 bits integer
buf_end:
.byte 10 # new line
.text
.globl _start
# GCD of two numbers.
# input: eax, ebx - two numbers
# output: eax - GCD
# uses: eax, ebx, edx
gcd2:
and %ebx, %ebx # is %ebx == 0 ?
jz gcd2_exit # %ebx == 0, go to exit and return %eax (GCD)
xor %edx, %edx # set %edx = 0 */
div %ebx # divide: %edx:%eax / %ebx, actually: %eax / %ebx
mov %ebx, %eax # drop quotient in %eax and keep previous %ebx in %eax
mov %edx, %ebx # put remainder in %ebx
jmp gcd2
gcd2_exit:
ret
# Print an unsigned integer in eax.
# input: eax - unsigned integer to print
# uses: eax, ebx, ecx, edx, edi, buffer
print:
mov $10, %ecx # set %ecx = 10 (radix)
mov $buf_end, %edi
next_digit:
xor %edx, %edx # set %edx = 0
div %ecx # divide: %edx:%eax / %ecx, actually: %eax / %ecx
# %edx is a remainder (0-9), it fits into %dl
add $48, %dl # get ASCII code: 0 => 48 = '0', 1 => 49 = '1'
dec %edi # put remainders going from the end of the buffer
mov %dl, (%edi)
# now eax is a quotient
and %eax, %eax # is quotient == 0 ?
jnz next_digit # quotient is not 0, go on
# printing the number:
mov $4, %eax # syscall `write'
mov $1, %ebx # write to stdout
mov %edi, %ecx # first character to write
mov $buf_end, %edx
sub %edi, %edx # edx is a number of characters to write (buf_end - edi)
inc %edx # + new line
int $0x80 # do syscall (print the number)
ret
# Convert string into unsigned integer
# input: esi - pointer to string
# output: ebx - unsigned integer
# uses: eax, ebx, ecx, edi, direction flag
str2uint:
xor %ecx, %ecx # it will be the string length
dec %ecx # ecx = -1 != 0 for repne
xor %eax, %eax # search for 0 (%eax = %al = 0)
mov %esi, %edi
cld # Search forward (std - backward)
repne scasb # search for 0 (in %al), incrementing edi, decrementing ecx
not %ecx # invert ecx to have the string length
dec %ecx # minus ending zero
xor %ebx, %ebx
str2uint_loop:
lodsb # going forward from esi
# HINT: assert '0' <= al <= '9'
lea (%ebx, %ebx, 4), %ebx # ebx = 4*ebx + ebx = 5*ebx ;-)
lea -48(%eax, %ebx, 2), %ebx # ebx = 2*ebx + %eax - 48
# ebx is multiplied by 10 each iteration,
# eax-48 will be multiplied at the next iteration ;-)
loop str2uint_loop
ret
# Entry point for the program
_start:
# Access command line, see:
# http://www.cin.ufpe.br/~if817/arquivos/asmtut/index.html
# Example: ./gcd-x86-linux 11 22 33
pop %ecx # Get the number of command-line options (4)
pop %esi # Get the pointer to the program name (./gcd-x86-linux),
dec %ecx # minus program name
jz exit # no arguments are given - exiting
xor %eax, %eax
gcd_loop:
pop %esi # get next command line argument
# Well, we used all registers, now we DO NEED stack :-)
push %ecx # save argument counter
push %eax # save current GCD
call str2uint # convert string at esi to integer at ebx
pop %eax # restore current GCD
call gcd2 # gcd of eax and ebx (returned by str2uint)
# now eax is a GCD
pop %ecx # restore argument counter
loop gcd_loop
call print # print eax with GCD
exit:
mov $1, %eax # exit syscall
xor %ebx, %ebx # exit code = 0
int $0x80
|