Assembly Language (finally!)

CS 301 Lecture, Dr. Lawlor

OK, so in the last two weeks, we've looked at bits, bit operations, hexadecimal, tables, and finally machine code (in excruciating detail).  Together, these are everything you need to know in order to understand assembly language.  Assembly language is, simply, a line-by-line copy of machine code transcribed into human-readable words.

For example, we've been using the "move into register 0" instruction (0xb8) a lot.  In an assembler, you can emit the same machine code with this little assembly language program:
mov eax,5

(Try this in NetRun now!)

The assembler (NASM, in this case) will then spit out the following machine code:
00000000 <foo>:
0: b8 05 00 00 00 mov eax,0x5
5: c3 ret
6: c3 ret
Note the middle column contains the same 0xb8 and so on that in HW2, we wrote by hand.  (The duplicate "ret" instructions are because NetRun always puts in a spare "ret" instruction at the end, in case you forget.)

The big advantage of using an assembler is that you don't need to remember all the funky arcane numbers, like 0xb8 or 0xc3 (these are "opcodes").  Intead, you remember a human-readable name like "mov" (short for "move").  This name is called an "opcode mnemonic", but it's always the first thing in a CPU "instruction", so I usually will say "the mov instruction" rather than "the instruction that the mov opcode mnemonic stands for".

There are several parts to this line:
Unlike C/C++, assembly is line-oriented, so the following WILL NOT WORK:
	mov eax,
Yup, line-oriented stuff is indeed annoying.  Be careful that your editor doesn't mistakenly add newlines to long lines of text!


A list of all possible x86 instructions can be found in: The really important opcodes are listed in my cheat sheet.  Most programs can be writen with mov, the arithmetic instructions (add/sub/mul), the function call instructions (call/ret), the stack instructions (push/pop), and the conditional jumps (cmp/jmp/jl/je/jg/...).   We'll learn about these over the next few weeks!


Here are the commonly-used x86 registers:
Each of these registers is available in several sizes:
Curiously, you can write a 64-bit value into rax, then read off the low 32 bits from eax, or the low 16 bitx from ax--it's just one register, but they keep on extending it!

rax: 64-bit
eax: 32-bit
ax: 16-bit

For example,
mov rcx,0xf00d00d2beefc03; load 64-bit constant
mov eax,ecx; pull out low 32 bits

(Try this in NetRun now!)

Arithmetic In Assembly

Here's how you add two numbers in assembly:
Here's the C/C++ equivalent:
int a = 3;
int c = 7;
a += c;
return a;
And finally here's the assembly code:
mov eax, 3
mov ecx, 7
add eax, ecx
(executable NetRun link)

Here are the x86 arithmetic instructions.  Note that they *all* take just two registers, the destination and the source. 
add eax,ecx
sub eax,ecx
imul eax,ecx
idiv eax,ecx
and eax,ecx
or eax,ecx
xor eax,ecx
not eax

Be careful doing these!  Assembly is *line* oriented, so you can't say:
    add (sub eax,ecx),edx
but you can say:
    sub eax,ecx
    add eax,edx

In assembly, arithmetic has to be broken down into one operation at a time!