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
.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 do
x &cur_valuein gdb to see what it is.
What is the suggested way to handle an
ifstatement. I've tried to do this in the
is_divisible_byfunction -- setting it to
$0by default and then 'overwriting' it if the
truecondition 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
endor whatever to be able to 'exit' things easily.
For labels within a main label (such as
set_trueetc.), what is a good way to name these? I see
gccuses something like
.L1029but 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_byfunction into another file and call it from within this main