本文简要说明x86架构及x86汇编语言的使用。
1978年Intel发布第一款16位微处理器8086(也称为iAPX86),之后又发布了80186、80286、80386、80486,因而该系列的CPU架构被称为x86。
下面将介绍80386的一些基本汇编指令,注意这里采用nasm语法。
现代的x86处理器(即80386之后)都有8个32位的通用寄存器(General Purpose Registers, GPR),


通常长的(32位)寄存器都可以被划分成小的读写块,如32位eax,16位ax,高8位ah,低8位al,注意nasm对标号大小写是敏感的,但对寄存器不会。
由于程序并不知道自己会被加载到哪,因此访存如果用绝对地址将会出错,在执行程序时就需要程序重定位这个操作。
在汇编中通过org指令实现,如org 0A100h代表该程序中的所有标号都以0A100h做偏移
将内存分段,程序只需管偏移地址/相对地址就好了 段地址:偏移地址
程序重定位通过设置代码段CS寄存器和数据段DS寄存器实现
在8086中,地址总线是20位的,需要将段寄存器左移4位(0x10h,相当于16进制左移1位)变为20位,然后再同偏移地址相加。
两种典型情况
0000H到000FH0000H到F000H段的划分是自由的,它可以起始于任何16字节对齐的位置,也可以是任意长度,只要不超过64KB。 正是由于段的划分非常自由,使得8086的内存访问也非常随意。 同一个物理地址,或者同一片内存区域,根据需要,可以随意指定一个段来访问它。
声明静态数据需要添加数据大小,包括1Byte的db,2B的dw,4B的dd,注意内存都是连续的,按字节(B)寻址
.DATA
var DB 64
var2 DB ? ; uninitialized byte
DB 10 ; no label, location is var2 + 1.
X DW ? ; 2B uninitialized
Y DD 30000;
str DB 'hello',0; 6 bytes starting at str
如果要访问某一大小的内存,则通过添加修饰词byte、word、dword实现(注意nasm是不存储变量类型的),这里的字(word)通常就指16位,双字(double word)32位
mov byte[ebx], 2
函数调用(call)堆栈组织

eax、ecx、edx)call会将返回地址eip压入栈中ebp推入栈,将esp的值拷贝入ebp
push ebp
mov ebp, esp
sub esp, 12函数调用例子如下
.486
.MODEL FLAT
.CODE
PUBLIC _myFunc
_myFunc PROC
; Subroutine Prologue
push ebp ; Save the old base pointer value.
mov ebp, esp ; Set the new base pointer value.
sub esp, 4 ; Make room for one 4-byte local variable.
push edi ; Save the values of registers that the function
push esi ; will modify. This function uses EDI and ESI.
; (no need to save EBX, EBP, or ESP)
; Subroutine Body
mov eax, [ebp+8] ; Move value of parameter 1 into EAX
mov esi, [ebp+12] ; Move value of parameter 2 into ESI
mov edi, [ebp+16] ; Move value of parameter 3 into EDI
mov [ebp-4], edi ; Move EDI into the local variable
add [ebp-4], esi ; Add ESI into the local variable
add eax, [ebp-4] ; Add the contents of the local variable
; into EAX (final result)
; Subroutine Epilogue
pop esi ; Recover register values
pop edi
mov esp, ebp ; Deallocate local variables
pop ebp ; Restore the caller's base pointer value
ret
_myFunc ENDP
END
由于x86指令实在太多,这里只摘录一些常用的指令
mov <ra>, <rb>push <r>;pusha:把AX,CX,DX,BX,SP,BP,SI,DI依次压入堆栈pop <r>;popa:把DI,SI,BP,SP,BX,DX,CX,AX依次弹出堆栈add <ra>, <rb>、sub:ra+=rbinc、deccmp <ra>, <rb>mul <r>:等价于mul ax, <r>and、or、xorshl、sal、shr、sar:h为逻辑,a为算术jmp <label>call <label>:过程调用ret:过程返回je <label>、jne:上一算术逻辑运算结果jl、jg:小于大于loop:cx不为0时循环cli/sti:关中断/开中断int <num>:中断iret:中断返回通过标号加方括号访问内存,如mov ax, [mem]
还有一些比较常见的特殊指令:
leave:等价于恢复堆栈,即mov esp,ebp\n\t pop ebpThe netwide assembler(NASM)是80x86和x86-64系列的汇编器
label: instruction operands ; comment

message db 'hello, world'
msglen equ ($-message)
zerobuf: times 64 db 0 ; executed n times
mov eax,[ebx*2+ecx+offset] db 'hello'等价于db 'h','e','l','l','o',0%define%macro prologue 1
push ebp
mov ebp,esp
sub esp,%1
%endmacro