x86 assembly -- problem w/ syntax for looping over array
ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
x86 assembly -- problem w/ syntax for looping over array
I am trying to run the following x86 assembly code, which is meant to add a set of numbers up and print out the result:
Code:
.section .data
message:
.ascii "The sum of the first 5 primes is \0"
numbers:
.long 2, 3, 5, 7, 11
.section .text
.globl _start
_start:
####################
# Print our message#
####################
movl $0, %edi # set index pointer to 0
movl $4, %eax # syscall -- write()
movl $1, %ebx # file descriptor '1' (STDOUT)
movl $message, %ecx # put start address of string in %ecx
movl $35,%edx # string is 35 chars long
int $0x80 # interrupt 80 -- Linux syscall
######################
# Add the numbers up #
######################
# %edi will hold our current position
# %ecx will hold the counter
# %edx will hold the current sum
movl $0, %edi # start at the first byte 0
movl $0, %edx # initialize sum variable to 0
movl $5, %ecx # set counter to 5 (because we have 5 numbers)
add_loop:
cmpl $0, %ecx # check to see if the counter is 0 yet
je print_sum # if so, we're done, so print the result
# otherwise, continue adding the numbers
addl $numbers(,%edi,4), %edx # add the next element to the sum in %edx
subl $1, %ecx # decrement the counter
jmp add_loop # go back to the top of the loop
print_sum:
movl $4, %eax # syscall -- write()
movl $1, %ebx # file descriptor '1' (STDOUT)
pushl %edx
movl %esp, %ecx # put start address of string in %ecx
movl $4, %edx # we're printing a 4 byte string
int $0x80 # interrupt 80 -- Linux syscall
################
# Exit Program #
################
movl $1, %eax # syscall -- exit()
movl $0, %ebx # return (0)
int $0x80 # interrupt 80 -- Linux syscall
My first problem is with the line addl $numbers(,%edi,4), %edx in the add_loop. This is obviously not syntactically correct, as I am getting an error trying to assemble it. I was trying to copy the syntax from a movl instruction that was shown earlier in the book I am reading, but obviously this does not work. How would I got about telling it to go through each of the numbers in $numbers and add 4 to %edi (i.e. move up 4 bytes to the next number)?
My second problem concerns the print_sum statement. Is this the correct way to push the sum held in %edx to the stack and then print it?
Any other suggestions on better assembly programming practices would be appreciated as well.
Thanks,
jrtayloriv
Last edited by jrtayloriv; 01-31-2008 at 10:19 PM.
My first problem is with the line addl $numbers(,%edi,4), %edx in the add_loop. This is obviously not syntactically correct, as I am getting an error trying to assemble it.
Try addl $numbers,(%edi,4),%edx
Quote:
My second problem concerns the print_sum statement. Is this the correct way to push the sum held in %edx to the stack and then print it?
I'll pass on this. I don't see anything wrong with it, but I would have to have a truly compelling reason to do this in assembler rather than C (or in a shell script, for that matter), so I am not up on the details of setting registers for linux system calls at that level.
[/QUOTE]
That didn't work -- when I did that, I got an error message stating:
Code:
test.s: Assembler messages:
test.s:60: Warning: scale factor of 4 without an index register
test.s:60: Error: suffix or operands invalid for `add'
addl only takes two operands, so wouldn't the way you recommended be giving it three? I'm assuming that's why the 'suffix or operands invalid' error is popping up. Or am I misunderstanding something about the syntax? Does the comma operator not work like in other languages?
I double-checked the book, and the way I had it was how the book mentioned it. I had figured that maybe the space before the comma that was inside the parentheses in $numbers(,%edi,4) was maybe defaulting to something like $numbers(DEFAULT_VALUE,%edi,4) ... was I wrong there? Was there a typo in the book?
Like I said before though, they were using movl on the list of numbers, not addl -- is there a different method for addl? How would I tell it to walk through the list of numbers here? Was I at least on the right track?
Quote:
I don't see anything wrong with it, but I would have to have a truly compelling reason to do this in assembler rather than C (or in a shell script, for that matter), so I am not up on the details of setting registers for linux system calls at that level.
I realize that this is not something that one would normally do in assembler -- but I am trying to learn assembler right now, and I can't just jump right into writing device drivers
Thanks,
jrtayloriv
Last edited by jrtayloriv; 02-01-2008 at 02:18 AM.
What assembler are you using? The syntax is quite a bit different from what I'm used to (MASM/TASM). I don't have my processor guide with me right now (and haven't seriously done asm in a few years), but I would do something like this to start with in MASM/TASM:
add dword ptr offset numbers[edi*4], edx
Actually, looking at that, something may be wrong. Can a scale be applied to an index, or only a base register? Hmmm, sure wish I was at home where my processor guide is!
This is done by using the ADDRESS_OR_OFFSET and the %INDEX portion. You can use any general-purpose register as the index register. You can also have a constant multiplier of 1, 2, or 4 for the index register, to make it easier to index by bytes, double-bytes, and words. For example, let's say that we had a string of bytes as string_start and wanted to access the third one (an index of 2 since we start counting the index at zero), and %ecx held the value 2. If you wanted to load it into %eax you could do the following:
movl string_start(,%ecx,1), %eax
But I can't figure out what exactly I'm doing wrong. I must not be understanding some basic concept about what's happening. Any ideas?
AT&T syntax, which is the destination, the first or second operand?
IIRC, you can't add directly to a memory location (read from, calculate, then write to a location). Once again, wish my processor book was here though...
I'm making progress. I have rewritten the section that does the adding as follows:
Code:
movl $0, %edx # initialize sum variable to 0
movl $5, %ecx # set counter to 5 (because we have 5 numbers)
movl $numbers, %eax # move address of $numbers into %eax
add_loop:
cmpl $0, %ecx # check to see if the counter is 0 yet
je print_sum # if so, we're done, so print the result
# otherwise, continue adding the numbers
addl (%eax), %edx # add the next element to the sum in %edx
subl $1, %ecx # decrement the counter
addl $4, %eax # point %eax to the next number in the list
jmp add_loop # go back to the top of the loop
And just for debugging purposes, I went ahead and just made the program exit with the sum stored in %edx as the return value (which I can retrieve w/ echo $?)
So now the program just goes jumps to this, after the counter is decremented to zero:
Code:
print_sum:
movl $1, %eax # syscall -- exit()
movl (%edx), %ebx # return value in %edx to caller -- i.e. shell
int $0x80 # interrupt 80 -- Linux syscall
And I went into gdb, set a breakpoint at the print_sum function, and did a dump of the registers:
I was telling it to use the value in %edx as an address in print_sum (The parentheses are like deferencing a pointer). I just had to change:
movl (%edx), %ebx
to
movl %edx, %ebx
But I still can't figure out why it's printing two 'T's. And now that I fixed the add_loop, I'd like to figure out how to actually print out the value in %edx to stdout, rather than having to check the return status.
The write() syscall is using up all of my general purpose registers, so I guess I'll have to figure out how to store something into a variable -- but I haven't gotten that far yet in the book ...
--jrtayloriv
Last edited by jrtayloriv; 02-01-2008 at 09:12 AM.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.