I have written the following basic assembly program to find the maximum number in a list divisible by a number. Here is what I have thus far:
# Program: given a list or integers and a factor
# Find the max number in the list that the factor divides
# For example: INPUT: [3,9,50,27], factor: 3 | OUTPUT: 27
.section .rodata
nums: .long 3,9,50,27,-1
factor: .long 3
.section .data
cur_value: .long -1
# first three args: %edi, %esi, %edx
.section .text
.globl _start
_start:
# SETUP
# %r8 will store the array index
# %r11 will store the max value
# %esi will store the factor/divisor.
mov $0, %r10d
mov $0, %r11d
mov factor, %esi
loop:
# get current value and store it in %rdi
# we'll also update our variable for `cur_value`
mov nums(, %r10d, 4), %edi
cmp $-1, %edi
je exit
movl %edi, cur_value
# Call the function and increment the aray index
call is_divisible_by
inc %r10d
# if it was NOT divisible (rax = False or 0) jump back to the beginning
cmp $0, %rax
je loop
# if it was divisible, check to make sure it's larger than the current max
cmp %r11d, cur_value
jl loop
mov cur_value, %r11d
jmp loop
exit:
mov %r11d, %edi
mov $60, %eax
syscall
is_divisible_by:
# Return 0 (false) if not divisible; 1 (true) if divisible
# A (dividend, %eax) / B (divisor)
# dividend needs to first be moved into eax
mov %edi, %eax
# divide by a register, immediate, or memory address
# this is unsigned (positive), use idiv for signed
div %esi
# the resultant integer quotient goes in %eax, and the remainder goes in %edx
# if %rdx is zero it means A is divisible by B: we don't care about %eax
mov $0, %eax
cmp $0, %edx
jne end
mov $1, %rax
end:
ret
It's compiled using:
$ as file.s -o file.o && ld file.o -o file
$ ./file; echo $?
# 27
Here are a few particular questions related to this:
Is it common to use named variables (such as
cur_value
in.section .data
) or not? I use them while learning a bit so it's easier to view the value of an easily-rememberable entity, i.e., I can just dox &cur_value
in gdb to see what it is.What is the suggested way to handle an
if
statement. I've tried to do this in theis_divisible_by
function -- setting it to$0
by default and then 'overwriting' it if thetrue
condition is met. -- but this seems pretty hacky. I suppose another way to do it would be something like:cmp $0, %edx je set_true set_false: mov $0, %eax jmp clean_up set_true: mov $1, %eax jmp clean_up clean_up: ret
Is it common to have end-labels on functions and such? I find myself often adding an
end
or whatever to be able to 'exit' things easily.For labels within a main label (such as
exit
orend
orset_true
etc.), what is a good way to name these? I seegcc
uses something like.L1029
but that seems not-too-friendly when writing my own and having to remember then.Would any of the above be better done 'on the stack' rather than using registers or named variables? I find it a bit more difficult to use the stack than registers as you cannot do something like
mov mem1, mem2
Finally, how could I extract the
is_divisible_by
function into another file and call it from within this mainfile.s
file?