Assembly course · Lesson 1
Registers and the first instruction
At the end you know registers and the instruction mov, and you have run a complete program and watched it step by step in the debugger. Prerequisite: Asmonite is unpacked, nothing else.
The idea: registers
A processor does not calculate with numbers in main memory but with numbers in registers: a few tiny, very fast storage places right inside the CPU. An assembly program largely consists of fetching numbers into registers, doing something with them there and writing them away again. Every register has a name that you write in the program.
The i386 has eight 32-bit general-purpose registers: eax, ebx, ecx, edx, esi, edi, ebp and esp. (esp is the stack pointer, ebp usually the frame pointer; leave both alone for now.) In addition there is the instruction pointer eip (it points to the next instruction) and the flags (from lesson 3).
The x64 has sixteen 64-bit registers: rax, rbx, rcx, rdx, rsi, rdi, rbp, rsp and r8 to r15. The lower 32 bits of a register have a name of their own: eax is the lower half of rax, r8d that of r8. (rsp is the stack pointer; leave it alone for now.) In addition there is the instruction pointer rip and the flags (from lesson 3).
ARM 32 has sixteen 32-bit registers r0 to r15. The last three have special roles and second names: r13 is sp (stack pointer), r14 is lr (link register: it holds the return address) and r15 is pc (the instruction pointer). For your own values you use r0 to r12. In this course ARM 32 means the Thumb-2 instruction set (.arch thumb) that Windows on ARM uses.
ARM 64 has 31 general-purpose 64-bit registers x0 to x30. The lower 32 bits of a register are called w0 to w30. x30 is also lr (it holds the return address). In addition there are the stack pointer sp and the instruction pointer pc; you cannot calculate with pc like with a register.
One of the registers is special: the program returns its result to the caller in a fixed register. For a Windows program it becomes the exit code, which you see on the command line or in the debugger.
| Architecture | Register for the result |
|---|---|
| i386 | eax |
| x64 | rax (eax is its lower half) |
| ARM 32 | r0 |
| ARM 64 | x0 (w0 is its lower half) |
The smallest program
Two instructions are enough: write a number into the result register and return.
; Lesson 1: the smallest program. EAX = 1, then back to Windows: the exit code is 1. .format pe console .arch i386 .code start: mov eax, 1 ret
; Lesson 1: the smallest program. RAX = 1, then back to Windows: the exit code is 1. .format pe console .arch x64 .code start: mov eax, 1 ret
; Lesson 1: the smallest program. R0 = 1, then back to Windows: the exit code is 1. .format pe console .arch thumb .code start: movs r0, #1 bx lr
; Lesson 1: the smallest program. X0 = 1, then back to Windows: the exit code is 1. .format pe console .arch arm64 .code start: mov x0, #1 ret
.format pe consoletells the assembler to make a Windows program for the console (it writes the header, the sections and everything else needed itself)..archchooses the processor. Both are directives: instructions for the assembler, not for the CPU..codestarts the section with instructions.start:is a label, a name for a place in the program. The program begins wherestartstands.mov eax, 1copies the number 1 intoeax. The destination is always on the left, the source on the right:mov destination, source.retreturns to whoever started the program (Windows);eaxbecomes the exit code.mov eax, 1copies the number 1 intoeax, the lower half ofrax. The destination is always on the left, the source on the right:mov destination, source. Writing to the lower half sets the upper half to 0, soraxis 1 afterwards.retreturns to whoever started the program (Windows).movs r0, #1copies the number 1 intor0; here too the destination is on the left. The#marks a number in the instruction. Thesat the end means: set the flags too (lesson 3); in the Thumb instruction set it is the short form of this instruction.bx lrjumps to the address inlr, that is back to whoever started the program (Windows).mov x0, #1copies the number 1 intox0; here too the destination is on the left. The#marks a number in the instruction.retjumps to the address inx30(lr), that is back to whoever started the program (Windows).
Try it in Asmonite
- Start Asmonite, create a new file and paste the code above (for the architecture you chose). Save it, for example as
first.asm. - Press Ctrl+F9: the assembler translates the program. The output window shows what it did; errors appear there with a line number.
- Press F8: the program starts and stops at the first instruction. On the right you see the registers. In the source file a marked line shows the next instruction to be executed.
- Press F8 once more: the first instruction runs. The register with the result now has the value 1, and because it changed it is marked red.
- Press F8 a third time: the return ends the program. The output window shows the exit code, here 1.
Your computer is (probably) not an ARM computer. ARM programs run on a Windows on ARM device; how to connect Asmonite to such a device is explained on the page Windows on ARM. Until then you can read and assemble the ARM programs and compare them with the Intel versions.
Your computer is (probably) not an ARM computer. ARM programs run on a Windows on ARM device; how to connect Asmonite to such a device is explained on the page Windows on ARM. Until then you can read and assemble the ARM programs and compare them with the Intel versions.
What you see
Before the first F8 the result register has some value that Windows left there. After the first F8 it holds the 1 (in red). The instruction pointer eiprippc has moved on by the length of the instruction. That is the whole program: one instruction changes the state of the CPU, then the next one has its turn.
Exercises
1. A different exit code
Change the number so that the program ends with the exit code 42. Check it in the debugger.
Solution
mov eax, 1 becomes mov eax, 42.
movs r0, #1 becomes movs r0, #42.
mov x0, #1 becomes mov x0, #42.
2. One register into another
First write the number 5 into a register other than the result register, then copy it with mov into the result register. Watch both registers with F8. How many instructions do you need?
Solution
mov ebx, 5
mov eax, ebx
mov ecx, 5
mov eax, ecx
movs r1, #5
mov r0, r1
mov x1, #5
mov x0, x1
Three instructions with ret, two of them mov. The second copies a register into a register: the destination is again on the left, the source on the right, and the source keeps its value (copying, not moving).
3. Brain teaser: swapping
Set eax to 3 and ebx to 4 (on ARM: the registers numbered 0 and 1) and swap their contents without losing a value. At the end the result register should hold 4, so the program ends with the exit code 4. You may use a third register.
Solution
As when swapping the contents of two glasses you need an empty one: value A into the free register, B into A, the free one into B.
mov eax, 3 mov ebx, 4 mov ecx, eax ; free = A mov eax, ebx ; A = B mov ebx, ecx ; B = free
mov eax, 3 mov ebx, 4 mov ecx, eax ; free = A mov eax, ebx ; A = B mov ebx, ecx ; B = free
movs r0, #3 movs r1, #4 mov r2, r0 ; free = A mov r0, r1 ; A = B mov r1, r2 ; B = free
mov x0, #3 mov x1, #4 mov x2, x0 ; free = A mov x0, x1 ; A = B mov x1, x2 ; B = free
Remember
- Registers are tiny, fast storage places in the CPU. Each has a name.
mov destination, sourcecopies; the destination is on the left.- The result of a program is in the fixed result register and becomes the exit code.
- The instruction pointer points to the next instruction; one instruction after the other changes the state of the CPU.
- With Ctrl+F9 you assemble, with F8 you go through the program step by step.