2
\$\begingroup\$

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:

  1. 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 do x &cur_value in gdb to see what it is.

  2. What is the suggested way to handle an if statement. I've tried to do this in the is_divisible_by function -- setting it to $0 by default and then 'overwriting' it if the true 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
    
  3. 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.

  4. For labels within a main label (such as exit or end or set_true etc.), what is a good way to name these? I see gcc uses something like .L1029 but that seems not-too-friendly when writing my own and having to remember then.

  5. 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

  6. Finally, how could I extract the is_divisible_by function into another file and call it from within this main file.s file?

\$\endgroup\$
1
  • \$\begingroup\$ As stated in the guidelines, please do not edit the question after you have received an answer. \$\endgroup\$
    – pacmaninbw
    Commented Sep 16, 2020 at 21:23

1 Answer 1

1
\$\begingroup\$

You have two independent conditions that must be satisfied before updating cur_val.

  1. The number must be divisible by factor
  2. 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.

\$\endgroup\$
5
  • \$\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\$ Commented 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 than call is_divisible_by, cmp & je will take. \$\endgroup\$
    – AJNeufeld
    Commented 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\$ Commented 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
    Commented Sep 18, 2020 at 0:10
  • \$\begingroup\$ @AJNeufedl -- I see, ok thanks for the feedback though! \$\endgroup\$ Commented Sep 18, 2020 at 2:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.