int foo(int a, int b) {
int c = a + b;
int d = a * b;
return c + d;
}
int main() {
return foo(4, 5);
}
does then you're more than ready to start diving into assembly. Using either Clang or GCC take your simple programs (like the one above) and compile them with the following options clang -O0 -S -f-no-asynchronous-unwind-tables foo.c
This will output a file called foo.s. Open it up and you should see the unoptimized assembly equivalent of the machine code the compiler would generate for your program (sans stuff involving linking). For the sake of brevity I'll omit it from this post.Start off with very very simple programs and examine their output. Change the source slightly and observe how the assembly changes. And as usual when learning new stuff, google everything that doesn't make sense. If you come across an instruction you've never seen before then google it, odds are just knowing what it does will make its purpose obvious for simple enough programs. Starting with simple stuff really is the key. Eventually you can even try writing your own version in assembly first and then compare it to what the compiler gives you for an equivalent C program. Sometimes the results will be very surprising and you'll learn something interesting.
One thing I will say you should read up on first is the System V ABI (http://wiki.osdev.org/System_V_ABI). Assuming you're on Linux or OSX. It's the function calling convention used by modern systems. You don't have to use it yourself necessarily, but when you examine compiler generated assembly or if you want to know how to call libc functions understanding it is crucial.
I'm by no means an expert on assembly, but this is what I did and I'm at a place now where I'm comfortable looking at most pieces of assembly code and following along. Writing it is still hard, but mostly because it takes so damn long and higher level languages have spoiled me.