I'm a newbie to 80386 assembly language. Currently struggling on a school assignment that asks to write a function in assembly language that will be called in a c program.
extern int count(char *string, char c);
I think I have a sense of how this should be done, but still struggling with choosing the right instruction(instruction ends with 'b', 'w' or 'l') and perhaps the "right" register, I know there are some that are reserved to certain purposes.
.text
.global count
count:
pushl %ebp # set up stack frame
movl %esp,%ebp # save %esp in %ebp
subl $12, %esp # automatic variables
movl $0, %eax # initialize %eax to 0
movl 8(%ebp), %esi # pointer to s
movb 12(%ebp), %bh # pointer to c
check:
movb (%esi), %bl # move the first char in s to %bl
cmp 0, %bl # if the char is \0
je done # job is done
cmp %bh, %bl # else compare the char to %bh
je found1 # if match increase the counter
incb %bl # else move to next char
jmp check
found1:
addl $1, %eax # found a match
incb %bl
jmp check # go back to the beginning of check
done:
movl %ebp, %esp # restore %esp from %ebp
popl %ebp # restore %ebp
ret
.end
My understanding of this program is that it should store the address of two values(string and char) into two registers. Then access the string char by char and compare it with the char stored in another register. If a match is found increase the return value in %eax, otherwise goes to the next char in the string until the end\0
is reached.
My program seems to be stuck in a loop as it does not crash either output a result.
Any help will be appreciated.
I don't think there is a real reason to save %esp
to %ebp
, or to subtract from %esp
. You do need to save %esi
. I think the a, b, c, and d registers can be safely lost, but if not (it's been some time since I used assembly), you need to save %ebx
as well.
(Update: as @NateEldredge pointed out, %ebx
has to be preserved - and I forgot to update the stack pointer. Yes, it has been too long).
count:
pushl %esi # save %esi as we use it
pushl %ebx
# "In assembly language, all the labels and numeric constants used
# as immediate operands (i.e. not in an address calculation like
# 3(%eax,%ebx,8)) are always prefixed by a dollar sign."
# https://flint.cs.yale.edu/cs421/papers/x86-asm/asm.html
movl 12(%esp), %esi # pointer to s
movb 16(%esp), %bh # char
# I think it's more common "xor %eax, %eax"
movl $0, %eax # initialize %eax to 0
check:
movb (%esi), %bl # move the current char in s to %bl
cmp $0, %bl # if the char is \0
je done # job is done
cmp %bh, %bl # else compare the char to %bh
je found1 # if match increase the counter
# We must increase the pointer to the character, not %bl
incl %esi # else move to next char
jmp check
found1:
addl $1, %eax # found a match
# incb %bl
incl %esi # move to next char
jmp check # go back to the beginning of check
done:
popl %ebx
popl %esi # restore %esi
ret
.end
You could also invert the test to save some instructions:
cmp %bh, %bl # else compare the char to %bh
jne notfound # if not match, skip incrementing
addl $1, %eax # found a match
notfound:
incl %esi # move to next char
jmp check