Although I've used assembly quite a bit for some simply things, I feel like I've never properly learnt how to write it idiomatically, by which I mean: which variables to use where, how best to structure loops and conditionals, how best/when to preserve registers over procedure calls, etc...
I wrote a small program which interfaces with the C standard library runtime, and will simply print the the amount of command line arguments followed by a list of all those arguments, for example:
$ ./sver Hello, this is a test!
The (6) arguments:
./sver
Hello,
this
is
a
test!
And here's the code itself:
.global main
.type main, @function
main:
mov %rsi, %r12
mov %rdi, %rsi
lea message(%rip), %rdi
xor %rax, %rax
pushq %rsi
call printf
popq %rsi
loop:
cmp $0, %rsi # If we've printed everything, then finish
jz end
mov 0(%r12), %rdi # Dereference r12 to get the current arg
pushq %rsi
call puts # Print one argument
popq %rsi
add $8, %r12 # Advance r12 to point at the next arg
dec %rsi
jmp loop
end:
mov $0, %eax
ret
.section .data
message:
.string "The (%u) arguments:\n"
# Explicitly say that I don't want an executable stack, otherwise GCC complains
.section .note.GNU-stack,"",@progbits
I'm compiling this with GCC.
Some aspects specifically that I'm unsure about:
- I'm using R12 to store a copy of RSI (which is just
argv
from C). I use it later when iterating to print all the arguments. However I feel like something about this is weird - is there a more idiomatic way of doing this? - Before/after calls to
printf
andputs
I need to preserve the value of RSI, since I use it as the argument counter. To do this I push/pop it from the stack, but perhaps it would be better to store it in some callee-saved register instead? But it still specifically has to be in RSI for printf to work, at least...
I tried writing a functionally equivalent program in C to look at its compiled assembly, but it's pretty incomprehensible, with lots of seemingly unnecessary jumping around etc.
I think my main problem is that I often don't know which register I should use to store a given value, or whether I should even put it on the stack.
Thanks!
# comments
, they're lovely, as are the symbolic labels. And please do offer a tag or Review Context that explains the particular processors you intend this code should run on. If you viewed the corresponding C code through the lens of godbolt.org, it wouldn't hurt to mention a link in your Question. \$\endgroup\$xor %rax, %rax
potentially not being as clear as it could be, however from what I've seen it seems pretty common in handwritten assembly - perhaps a macro would be nice though! And yes, I'm surprised I didn't mention the processor this will run on; it's for x86-64, btw. For viewing the corresponding C code's assembly, I actually usedgcc -S
, so it wasn't so easy to link unfortunately. Thanks! \$\endgroup\$