summaryrefslogtreecommitdiff
path: root/assembler/gcd-x86-linux.s
blob: aa46fd33b2e4357312bcad891e30fdc30578e8af (plain)
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