-
Notifications
You must be signed in to change notification settings - Fork 0
/
gcd-x86.S
178 lines (154 loc) · 4.95 KB
/
gcd-x86.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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
# This program is for Intel x86 arch (32 bits) running Linux, Solaris or FreeBSD.
# Written for GNU Assembler (as) with AT&T syntax
#
# Synopsis:
#
# $ gcc -nostdlib -m32 [-D OS=LINUX] gcd-x86.S -o gcd-x86-linux
# or
# $ cpp [-D OS=LINUX] gcd-x86.S | as -32 -o gcd-x86-linux.o \
# && ld -m elf_i386 gcd-x86-linux.o -o gcd-x86-linux
#
#
# $ gcc -nostdlib -m32 -D OS=SOLARIS gcd-x86.S -o gcd-x86-solaris
# or
# $ cpp -D OS=SOLARIS gcd-x86.S | gas -32 -o gcd-x86-solaris.o \
# && gld -m elf_i386_sol2 gcd-x86-solaris.o -o gcd-x86-solaris
#
#
# $ clang -nostdlib -m32 -D OS=FREEBSD gcd-x86.S -o gcd-x86-freebsd
#
#define LINUX 1
#define SOLARIS 2
#define FREEBSD 3
#ifndef OS
#define OS LINUX
#endif
.data
# Buffer for output:
buffer:
.space 32 # enough for a 32-bit 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 $buf_end, %edx
sub %edi, %edx # edx is a number of characters to write (buf_end - edi)
inc %edx # + new line
#if OS == LINUX
mov $4, %eax # syscall `write'
mov $1, %ebx # write to stdout
mov %edi, %ecx # first character to write
int $0x80 # do syscall (print the number)
#elif OS == SOLARIS
mov $4, %eax # syscall `write'
push %edx
push %edi # first character to write
push $1 # write to stdout
push $0 # dummy
int $0x91 # do syscall (print the number)
add $16, %esp # clean stack 16 = 4 pushs * 4 bytes (32 bits!)
#elif OS == FREEBSD
mov $4, %eax # syscall `write'
push %edx
push %edi # first character to write
push $1 # write to stdout
push $0 # dummy
int $0x80 # do syscall (print the number)
add $16, %esp # clean stack 16 = 4 pushs * 4 bytes (32 bits!)
#else
#error bad OS.
#endif
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.
# 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:
#if OS == LINUX
mov $1, %eax # exit syscall
xor %ebx, %ebx # exit code = 0
int $0x80
#elif OS == SOLARIS
mov $1, %eax # exit syscall
push $0 # exit code = 0
push $0 # dummy
int $0x91
#elif OS == FREEBSD
mov $1, %eax # exit syscall
push $0 # exit code = 0
push $0 # dummy
int $0x80
#else
#error bad OS.
#endif