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 to0ドル
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?
-
\$\begingroup\$ As stated in the guidelines, please do not edit the question after you have received an answer. \$\endgroup\$pacmaninbw– pacmaninbw ♦2020年09月16日 21:23:43 +00:00Commented Sep 16, 2020 at 21:23
1 Answer 1
You have two independent conditions that must be satisfied before updating cur_val
.
- The number must be divisible by
factor
- The number must be larger than
cur_val
If the first test fails, you don't bother doing the second test.
How many instruction (or better cycles) does it take to do each test?
Given a long non-monotonic list, you may save considerable time simply by reversing these two tests.
-
\$\begingroup\$ sorry I've updated the question. The elements in the list can be in any order not just a sorted list. Also, regarding
How many instruction (or better cycles) does it take to do each test
-- how can I find out how many cycles a section of code takes? I'm very new to assembly and have always wondered about that? \$\endgroup\$samuelbrody1249– samuelbrody12492020年09月16日 18:45:18 +00:00Commented Sep 16, 2020 at 18:45 -
\$\begingroup\$ That is a very difficult question, and can depends on CPU version, cache, CPU -vs- Memory bus clock speed, and so on. You'd have to consult the CPU spec sheet, motherboard configuration. But a fair guess is
cmp
&jl
takes way less thancall is_divisible_by
,cmp
&je
will take. \$\endgroup\$AJNeufeld– AJNeufeld2020年09月16日 21:08:25 +00:00Commented Sep 16, 2020 at 21:08 -
\$\begingroup\$ I see. Do you want to add a bit of code to your answer with how you'd suggest implementing it? \$\endgroup\$samuelbrody1249– samuelbrody12492020年09月17日 23:32:44 +00:00Commented Sep 17, 2020 at 23:32
-
\$\begingroup\$ @samuelbrody1249 Sorry, no. I'm reasonably proficient reading code, but writing assembly I haven't done since 80486 days. \$\endgroup\$AJNeufeld– AJNeufeld2020年09月18日 00:10:05 +00:00Commented Sep 18, 2020 at 0:10
-
\$\begingroup\$ @AJNeufedl -- I see, ok thanks for the feedback though! \$\endgroup\$samuelbrody1249– samuelbrody12492020年09月18日 02:15:26 +00:00Commented Sep 18, 2020 at 2:15