It's usually best to think about a high level language version first. If the number has n
digits and is stored in array a
, then we'd want:
char *p = a;
unsigned val = 0;
while (n > 0) {
n--;
val = 10 * val + (*p++ - '0');
}
So let's say %esi
is p
above %eax
is val
, and %ecx
is n
.
Translate line by line to keep it as simplistic and literal as possible:
movl $n, %ecx
movl $a, %esi
xorl %eax, %eax # val = 0
eval_while_cond:
testl %ecx, %ecx # if (n <= 0)
jle done # goto done
subl $1, %ecx # n--
movl %eax, %ebx # tmp1 = val
imul $10, %ebx # tmp1 = 10 * tmp1
movzbl (%esi), %eax # tmp2 = *p
addl $1, %esi # p++
subl $'0, %eax # tmp2 = tmp2 - '0'
addl %eax, %ebx # val = tmp2 + tmp1
jmp eval_while_cond
done:
# result is now in %eax
Note this is just a general methodology for getting functional assembly language. This isn't optimized in any way. The tricks needed to get optimal performance are a whole other topic.
The other options is to use a compiler (e.g. https://godbolt.org/z/WzGYjePzx) with light optimization like -O1, -Og, or -Os to get somewhat-readable asm (although in this case they use not-totally-obvious tricks with LEA to multiply by 10 and subtract '0'
(48
), or they make a mess with weird loop conditions.) See also How to remove "noise" from GCC/clang assembly output?