Basic Assembly language
Assembly Language: Getting Started
So it occurred to me the other day that most people who visit my blog probably don't know enough assembly to fully understand my tutorials. This is what lead me to wanting to write a series of tutorials that focuses only on teaching this language. I have gone over a few things so far, but not nearly enough. Now I'm only planning on covering the basics because a majority of software don't use any of the advanced instruction sets. This is only because not all processors support these instruction sets, so I'm going to avoid mentioning things like AES, SSE, etc.
Now lets get started with my first assembly language tutorial. Apologies If this is kind of long, I want to cover some important things so we can expand of them more later. I'll keep it simple though by just going to cover how to create a program that returns a value. Also these tutorials will assume you have a 64-bit processor. There is quite a difference between IA-64 and IA-32 assembly, but I'm not going to go into it.
Lets talk a bit about assembly before we jump right into the code. Assembly language is the closet thing you can get to pure machine code that is still readable. Each instruction is assembled down into binary which can be executed by the CPU. There are two different syntax for assembly code, Intel's and AT&T's. I prefer using Intel's syntax since the disassembled code in IDA Pro is also in this notation. The assembler we're using has the ability to use both, so you can specify whichever you fell more comfortable with. It doesn't matter which one you use, because the general layout of assembly remains the same. Code is broken up into two segments, data and text. The text section contains all the instructions that are executed, and the data sections contains all the data that's used during the duration of the program.
Assembly instructions are also called operational codes, or opcodes. I'll be introducing a few of these opcodes in each of the tutorials. The CPU has a decoder in it that is capable of determining which opcode to execute based on the line of binary that the instruction pointer is currently pointing to. Modern day CPU's use much more advanced techniques, like branch prediction, multiple threads, and out-of-order execution, in order to maximize computing performance. But for now we're going to assume that these things don't exist. Lets say that our computer can only do four elementary tasks per every machine cycle: fetch the current instruction, increment the instruction pointer, decoder the instruction, and execute that instruction. Based on this, we can assume that every instruction happens in order and only takes one clock tick to execute.
The internal CPU registers are general purpose registers that are often used in assembly code. For compatibility reasons, the old 16-bit and 32-bit register from earlier generations are still accessible, just under slightly different names. Here's how the 64-bit accumulator register, rax, is broken up into smaller registers.
The contents of the entire rax register aren't erased when operations are preformed on ax, ah, or al. They are however erased when preforming operations on rax and eax. This'll become important later. So here's what the value in rax is after preforming operations on it's smaller registers:One more thing to mention before we get started. Modern CPUs also use load/store procedures when dealing with data. This means that you never do any direct operations to data in the RAM. Instead you have to load the value at that RAM address into one of the internal CPU registers, perform the operation on that register, then store the value in that register to that RAM address. If you think about it, this load/store idea significantly reduces the number of instruction supported by the CPU. This is one of the characteristic modern processors inherited from the reduced instruction set computing, RICS, architecture.