汇编语言
一、操作数格式
操作数被分为三种类型
- 立即数(immediate): 用来表示常数值,在ATT格式的汇编代码中,立即数的书写方式是‘$’后面跟一个C标准整数,如$-577或$0x1F
- 寄存器(register): 标志某个寄存器的内容,16个寄存器的低位1字节、2字节、4字节、8字节中的一个作为操作数
- 内存引用:根据计算出来的地址访问某个内存位置
下表为操作数格式
对于下表中出现的符号,有以下定义:
- ra : 表示任意寄存器a
- R[ra] : 引用ra的值
- Mb[Addr]: 表示对储存在内存中从地址Addr开始的b个字节的引用
- Imm(rb, ri, s): 内存引用,有效地址被计算为 Imm + R[rb] + R[ri] s*
类型 | 格式 | 操作数值 | 名称 |
---|---|---|---|
立即数 | $Imm | Imm | 立即数寻址 |
寄存器 | ra | R[ra] | 寄存器寻址 |
储存器 | Imm | M[Imm] | 绝对寻址 |
储存器 | (ra) | M[R[ra]] | 间接寻址 |
储存器 | Imm(rb) | M[Imm + R[ri]] | (基址+偏移量)寻址 |
储存器 | (rb, ri) | M[R[rb] + R[ri]] | 变址寻址 |
储存器 | Imm(rb, ri) | M[Imm + R[rb] + R[ri]] | 变址寻址 |
储存器 | (,ri, s) | M[R[ri] s*] | 比例变址寻址 |
储存器 | Imm(,ri, s) | M[Imm + R[ri] s*] | 比例变址寻址 |
储存器 | (rb, ri, s) | M[R[rb] + R[ri] s*] | 比例变址寻址 |
储存器 | Imm(rb, ri, s) | M[Imm + R[rb] + R[ri] s*] | 比例变址寻址 |
二、数据传送指令
1. MOV 类
指令 | 效果 | 描述 |
---|---|---|
MOV S, D | D ← S | 传送 |
movb | 传送字节 | |
movw | 传送字 | |
movl | 传送双字 | |
movq | 传送四字 | |
movabsq I, R | R ← I | 传送绝对四字 |
指令 | 效果 | 描述 |
---|---|---|
MOVZ S, R | R ← S(零扩展) | 以零扩展进行传输 |
movzbw | 将做了零扩展的字节传送到字 | |
movzbl | 将做了零扩展的字节传送到双字 | |
movzwl | 将做了零扩展的字传送到双字 | |
movzbq | 将做了零扩展的字节传送到四字 | |
movzwq | 将做了零扩展的字传送到四字 |
指令 | 效果 | 描述 |
---|---|---|
MOVS S, R | R ← S(符号扩展) | 传送符号扩展的字节 |
movsbw | 将做了符号扩展的字节传送到字 | |
movsbl | 将做了符号扩展的字节传送到双字 | |
movswl | 将做了符号扩展的字传送到双字 | |
movsbq | 将做了符号扩展的字节传送到四字 | |
movslq | 将做了符号扩展的双字传送到四字 | |
ctlq | %rax ← %eax(符号扩展) | 把%eax符号扩展到%rax |
2. 压入与弹出栈数据
指令 | 效果 | 描述 |
---|---|---|
pushq S | R[%rsp] ← R[%rsp] - 8; M[R[%rsp]] ← S |
将四字压入栈 |
popq D | D ← M[R[%rsp]]; R[%rsp] ← R[%rsp] + 8 |
将四字弹出栈 |
三、x86_64的寄存器
一个x86-64 的中央处理单元(CPU)包含一组16个存储64位值的通用目的寄存器。这些寄存器用来存储整数数据和指针。下图显示了这16个寄存器。它们的名字都以%r开头,不过后面还跟着一些不同命名规则的名字,这是由于指令集历史演化造成的。最初的8086中有8个16位的寄存器,即%ax到%sp。每个寄存器都有特殊用途。扩展到IA32架构时,这些寄存器也扩展成32位寄存器,标号从%eax到%ebp。扩展到x86-64后,原来的8个寄存器扩展成64位,标号从%rax到%rsp。除此之外,还增加了8个新的寄存器,它们的标号是按照新的命名规则制定的:从%r8到%r15。
四、算数与逻辑操作
1. 整数算数操作
图4-1列出了x86-64的一些整数和逻辑操作。大多数操作都分成了指令类,这些指令类有各种带不同大小操作的变种(只有leaq
没有其他大小的变种)。例如,指令类ADD由四条加法指令组成:addb
、addw
、addl
、addq
。
事实上,给出的每个指令类都有对这四种不同大小数据的指令。这些指令操作被分为四种:加载有效地址、一元操作、二元操作和移位。
2. 特殊的算术操作
两个64位有符号或无符号整数相乘得到的乘积需要128位来表示。x86-64指令集对128位(16字节)数的操作提供有限的支持。图 4-2 描述的是支持产生两个64位数字的全128位乘积以及整数除法的指令。
imulq指令有两种不同的形式,一种是“双操作数”乘法指令,它从两个64位操作数产生一个64位乘积。此外,x86-64指令集还提供了两条不同的“单操作数”乘法指令,以计算两个64位值的全128位乘积——一个是无符号数乘法(mulq),另一个是补码乘法(imulq)。这两条指令都要求一个参数必须在寄存器%rax
中,而另一个作为指令的源操作数给出。然后乘积存放在**寄存器%rdx(高64位)和%rax(低64位)中。汇编器能够通过计算操作数的数目,分辨出想用哪条指令。