X86 assembly language
|
- The title of this article is incorrect because of technical limitations. The correct title is x86 assembly language.
x86 assembly language is the assembly language for the x86 class of processors, which includes Intel's Pentium series and AMD's Athlon series.
Contents |
x86 CPU design
The x86-processor design is CISC; however, since the end of the 1990s the internal architecture moved towards being more of a RISC or VLIW design. Modern x86-processors translate their instructions to RISC-like microcodes before they execute them, giving the x86 a slightly more superscalar design as several microcodes can easily be made to execute at once. This behaviour is however invisible to the assembly programmer.
A modern x86-processor has 7 notional stages in its pipeline.
stage 1: fetch the code stage 2: decode the code stage 3: register renaming stage 4: translate into microcodes stage 5: execute the microcodes stage 6: collect and summarize the results (out of order-stage) stage 7: save the results
However, most actual processors have more than 7 stages - for example, the Pentium 4 has a 20-stage deep pipeline - which may not be (though are usually) in exactly this order either. In between steps 4 and 6, the processor behaves more like a RISC/VLIW processor than a CISC processor.
Operation modes
There are several modes of processor operation; most notably real mode and protected mode for the i386 architecture, and long mode for newer processors. The language is essentially the same but involves different ways of accessing memory and thus employs different programming strategies.
For information on the assembly language within a respective mode, see:
Real mode
Real mode is mostly 16-bit, but since the 80386 it is possible to use 32-bit registers in this mode. It is also possible to enable partial 32-bit addressing in real mode through a bug/feature that appears under certain conditions when switching from protected mode back to real mode. Some DOS extenders make use of this to make it possible to access more than 1 megabyte of RAM. This bug-mode is sometimes called unreal mode by assembly programmers.
Protected mode
Protected mode (or 386 protected mode) enables full 32-bit addressing, paging, memory protection, hardware-support for multitasking, a few more registers and some new instructions to handle the 32-bit addressing. The instruction set in protected mode is perfectly backward compatible with the one used in real mode. 16-bit protected mode also exists, but is almost never used. It was used in early operating systems that needed memory protection. The mode delivers 24-bit addressing, which gives a maximum capability of 16 megabytes of memory. Some early Unix operating systems and OS/2 used this mode.
Long mode
Long mode is a mode that enables 64-bit addressing, 64-bit extensions of most registers and some new 64-bit registers as well. It is mostly an extension of the 32-bit instruction set, but some instructions have been replaced by new ones, making the resulting instruction set not entirely backwards compatible.
x86-family processors boot into real mode for backward compatibility with the older 8086 class of processors. Typically, the operating system is responsible for switching to protected mode if it so wishes. Recently, long mode has been created as a 64-bit environment for the x86-processors. Processors with the ability to change into long mode are said to belong to the x86-64-family. AMD Athlon64 is one x86-64 processor. To switch to long mode, the processor has to first switch from real mode to protected mode, and then to long mode. This behaviour guarantees backward compatibility.
The way x86-processors can change their processor-mode is remotely similar to reconfigurable computing.
Itanium-processors can also run x86-code in firmware, with significant performance degradation. Some people claim that the Itanium emulates the x86-code while others says that it does not. It is probably just a matter of definition.
Instruction overview
Since the x86 class of processors are CISC, they offer a large number of various instructions. Some of the newer processors aren't fully Cisc, instead the CISC instructions are translated to RISC microinstructions as the processor is running. However, if you view the processor as a "black box" where you don't know the internals, the x86 can be seen as a CISC processor.
Mathematical instructions
x86 assembly has the standard mathematical operations, add, sub, mul, with idiv; the logical operators and, or, xor, neg; bitshift arithmetic and logical, sal/sar,shl/shr, and others.
Data manipulation instructions
x86 assembly, with respect to data manipulation such as retrieving data from memory, and stack manipulation (however stack manipulation need not be done using these commands), provides commands for dealing with these tasks, such as for storing data, stosb, stosw; moving data, movsb, movsw; for stack manipulation push, pusha (all registers), pushf (flags), pop, popa, popf
Programming flow
As with virtually all assembly languages, x86 assembly has an unconditional jump operation, jmp
, as well as several conditional jumps, including je
(jump on equality), jn
(jump on inequality), jg
(jump on greater than, signed), jl
(jump on less than, signed), ja
(jump on above/greater than, unsigned), jb
(jump on below/less than, unsigned). These conditional operations are based on the state of specific bits in the (E)FLAGS register. Many arithmetic and logic operations set, clear or complement these flags depending on their result. The comparison cmp
(compare) and test
instructions set the flags as if they had performed a substraction or a bitwise AND operation, respectively, without altering the values of the operands. There are also instructions such as clc
(clear carry flag) and cmc
(complement carry flag) which work on the flags directly.
Each jump operation has three different forms, depending on the size of the operand. A short jump uses an 8-bit signed operand, which is a relative offset from the current instruction. In real mode or 16-bit protected mode, a near jump uses a 16-bit or unsigned operand as an address relative to the current segment base; in 32-bit protected mode, a near jump is a 16-bit or 32-bit signed relative offset similar to a short jump. A far jump is one that uses the full segment base:offset value as an absolute address. There are also indirect and indexed forms of each of these.
In addition to the simple jump operations, there are the call
(call a subroutine) and ret
(return from subroutine) instructions. Before transferring control to the subroutine, call
pushes the segment offset address of the instruction following the call
onto the stack; ret
pops this value off the stack, and jumps to it, effectively returning the flow of control to that part of the program. In the case of a far call
, the segment base is pushed following the offset.
There are also two similar instructions, int
(interrupt), which saves the current register values on the stack, then performs a far call
, except that instead of an address, it uses an interrupt vector, an index into a table of interrupt handler addresses. The matching return from interrupt instruction is iret
, which restores the register values after returning. Soft Interrupts of the type described above are used by some operating systems for system calls, and can also be used in debugging hard interrupt handlers. Hard interrupts are triggered by external hardware events.