汇编指令是机器指令的助记符,与机器指令一一对应。汇编存在多种形式,8086CPU目前主流的有
AT&T
和INTEL
两种,Windows使用INTEL语法,UNIX平台的编译器一直使用AT&T语法。本文以8086CPU为例,主要介绍AT&T
语法的基础汇编指令。
1. 寄存器
CPU由运算器、控制器和寄存器等构成,寄存器用于进行数据存储。8086有14个寄存器,这14个寄存器可按用途分为:8个通用寄存器、指令指针、标志寄存器和段寄存器四类。
E--
表示32位寄存器,R--
表示64位寄存器。
1.1 通用寄存器
寄存器 | 说明 |
---|---|
EAX(累加寄存器) | 常用于运算,在乘除等指令中指定用来存放操作数,用于I/O指令传送数据。 |
EBX(基址寄存器) | 常做内存数据的指针(地址索引),用于访问内存。 |
ECX(计数器) | 用于保存计算值,在移位、循环和串处理指令中做隐含的计数器。 |
EDX(数据寄存器) | 进行乘、除运算时作为默认的操作数参与运算,也用于存放I/O的端口地址。 |
ESI(源变址寄存器) | 可用于存放相对DS段的源变址指针,常用作内存数据指针和源字符串指针。 |
EDI(目的变址寄存器) | 存放存储单元在段内的偏移量,用它们可实现多种存储器操作数的寻址方式。 |
ESP(堆栈指针寄存器) | 只做堆栈的栈顶指针,不能用于算术运算和数据传送。 |
EBP(基址指针寄存器) | 只用做堆栈指针,可以访问堆栈内任意地址。 |
通用寄存器中EAX
、EBX
、ECX
和EDX
都属于数据寄存器,他们都可以再分为独立的寄存器。这里以EAX
为例。
EAX
是32位的寄存器;AX
是16位的寄存器(EAX
的低16位);AH
是8位寄存器(AX
的高8位);AL
是8位寄存器(AX
的低8位)。
1.2 指令指针
32位CPU把指令指针记为EIP
扩展到32位,在EIP的低16位与CPU中的IP作用相同。
指令指针用于存放下次将要执行的指令在代码段中的偏移量,在具有预取功能的系统中,下次要执行的指令通常已被预取到指令队列中,除非发生转移情况。
1.3 标志寄存器
标志寄存器称为FLAG
,也称为PSW
(Processor Status Word)。共其中有6个运算结果状态标志位:OF、SF、ZF、AF、PF、CF,及3个控制标志位DF、IF、TF。
运算结果状态标志位
-
进位标志CF(Carry Flag):进位标志CF主要用来反映运算是否产生进位或借位。如果运算结果的最高位产生了一个进位或借位,那么,其值为1,否则其值为0。
-
奇偶标志PF(Parity Flag):奇偶标志PF用于反映运算结果中“1”的个数的奇偶性。如果“1”的个数为偶数,则PF的值为1,否则其值为0。利用PF可进行奇偶校验检查,或产生奇偶校验位。
-
辅助进位标志AF(Auxiliary Carry Flag):当发生低字节向高字节进位或借位时,或低4位向高4位进位或借位时,辅助进位标志AF的值被置为1,否则其值为0。
-
零标志ZF(Zero Flag):零标志ZF用来反映运算结果是否为0。如果运算结果为0,则其值为1,否则其值为0。在判断运算结果是否为0时,可使用此标志位。
-
符号标志SF(Sign Flag):符号标志SF用来反映运算结果的符号位,它与运算结果的最高位相同,所以SF也就反映运算结果的正负号。运算结果为正数时,SF的值为0,否则其值为1。
-
溢出标志OF(Overflow Flag):溢出标志OF用于反映有符号数加减运算所得结果是否溢出。如果运算结果超过当前运算位数所能表示的范围,则称为溢出,OF的值被置为1,否则,OF的值被清为0。
状态控制标志位
状态控制标志位是用来控制CPU操作的,它们要通过专门的指令才能使之发生改变。
-
追踪标志TF(Trap Flag):当追踪标志TF被置为1时,CPU进入单步执行方式,即每执行一条指令,产生一个单步中断请求。这种方式主要用于程序的调试。指令系统中没有专门的指令来改变标志位TF的值,但程序员可用其它办法来改变其值。
-
中断允许标志IF(Interrupt-enable Flag):中断允许标志IF是用来决定CPU是否响应CPU外部的可屏蔽中断发出的中断请求。但不管该标志为何值,CPU都必须响应CPU外部的不可屏蔽中断所发出的中断请求,以及CPU内部产生的中断请求,CPU的指令系统中也有专门的指令来改变标志位IF的值。
-
方向标志DF(Direction Flag):方向标志DF用来决定在串操作指令执行时有关指针寄存器发生调整的方向。
32位标志寄存器增加的标志位
-
I/O特权标志IOPL(I/O Privilege Level):I/O特权标志用两位二进制位来表示,也称为I/O特权级字段。该字段指定了要求执行I/O指令的特权级。如果当前的特权级别在数值上小于等于IOPL的值,那么,该I/O指令可执行,否则将发生一个保护异常。
-
嵌套任务标志NT(Nested Task):嵌套任务标志NT用来控制中断返回指令IRET的执行,当NT=0,用堆栈中保存的值恢复EFLAGS、CS和EIP,执行常规的中断返回操作,当NT=1,通过任务转换实现中断返回。
-
重启动标志RF(Restart Flag):重启动标志RF用来控制是否接受调试故障。RF=0时表示“接受”调试故障。
-
虚拟8086方式标志VM(Virtual 8086 Mode):如果该标志的值为1,则表示处理机处于虚拟的8086方式下的工作状态,否则,处理机处于一般保护方式下的工作状态。
1.4 段寄存器
寄存器 | 说明 |
---|---|
ECS(代码段寄存器) | 代码段的段值 |
EDS(数据段寄存器) | 数据段的段值 |
EES(附加段寄存器) | 附加数据段的段值 |
ESS(堆栈段寄存器) | 堆栈段的段值 |
2. AT&T与INTEL区别
AT&T
和INTEL
的区别在语法表示上,没有质的区别。
-
运算表达式的顺序相反:
AT&T:
<cmd> <src> <tar>
INTEL:
<cmd> <tar> <src>
-
赋值符号不同:
**AT&T:**使用
->
赋值**INTEL:**使用
=
赋值 -
寄存器和立即数的书写方式不同:
**AT&T:**寄存器前需加
%
,立即数前加$
。**INTEL:**直接书写寄存器或立即数。
-
括号不同:
**AT&T:**使用圆括号
()
。**INTEL:**使用方括号
[]
。 -
指令操作码后缀不同:
**AT&T:**操作码后追加一个字母,
l
表示长整数(32位),w
表示字(16位),b
表示字节(8位)。**INTEL:**在操作数前面加对应指令,
dword ptr
表示长整数,word ptr
表示字,byte ptr
表示字节。示例:
// AT&T movl (%ebx),%eax // INTEL Mov eax, dword ptr [ebx]
-
间接寻址方式不同:
**AT&T:**间接寻址指令格式:
%segreg:disp(base,index,scale)
。**INTEL:**间接寻址指令格式:
segreg:[base+index*scale+disp]
。示例:
// AT&T Subl -0x20(%ebx,%ecx,0x4),%eax // INTEL sub eax,[ebx+ecx*4h-20h]
3. 常见汇编指令
以下介绍的语法都是AT&T
语法,除非有特
3.1 数据传送指令
这些指令主要在寄存器与内存、寄存器与输入/输出端口之间传送数据。
1. mov
数据传送指令
AT&T | INTEL | 说明 |
---|---|---|
movl 1, %eax | MOV EAX, 1 | 将自然数1传送到eax寄存器 |
movl %ebx,%eax | MOV EAX,EBX | 将ebx的值传给ebx寄存器 |
2. pop
出栈用一个寄存器接收数据
AT&T | 说明 |
---|---|
pop %eax | 将栈顶数据弹出到eax寄存器 |
3. push
将一个寄存器中的数据入栈
AT&T | 说明 |
---|---|
push1 %ebp | 将ebp寄存器的输入压入栈 |
3.2 算数运算指令
包括算术基本四则运算、浮点运算、数学运算(正弦、反弦)等。
1. add
加法运算
AT&T | 说明 |
---|---|
add 3, %eax | 将自然数3与eax寄存器中的数相加,结果存储在eax中 |
2. inc
加1指令
AT&T | 说明 |
---|---|
inc %ebx | 对ebx寄存器中的数加1 |
3. dec
减1指令
AT&T | 说明 |
---|---|
dec %ebx | 对ebx寄存器中的数减1 |
3.3 逻辑运算指令
与、或、非、左移、右移等指令。
3.4 串指令
连续空间分配,连续空间取值,传送等。高级语言的字符串即是通过串指令实现。
3.5 程序转移指令
if...else...
判断、for
循环、while
循环、函数调用等都依靠程序转移指令实现。