参考: 清华大学《汇编语言程序设计》课程, 主讲教师:张悠慧
汇编程序员中的CPU包括:
指令寄存器,即PC, 它保存了下一条指令的地址, 使用EIP(x86-32)或RIP(x86-64)访问。
寄存器堆:各种寄存器;
条件码: 用于存储最近执行的指令的结果状态信息, 也用于条件跳转指令的判断;
以AT&T汇编格式(即linux系统下)代码的格式;
(在汇编语言中没有数据类型,一般采用汇编指令的后缀进行区分)
在汇编语言中, 寄存器名字前加 %, 立即数前加 $,
寻址方式与数据传输指令
寻址方式:
间接寻址: (R), 在寄存器R中指定内存的地址, 如: mov1 (%ecx), %eax
基址+偏移量 寻址:D(R), 在寄存器R中指定基址,常数D给出偏移量, 如:mov 8(%ebp), %edx
通用形式: D(Rb, Ri, S), 寻找内存地址为 Rb + S * Ri + D处的数据。
其中,D表示地址偏移常量; Rb表示基址寄存器,它为8个通用寄存器之一; Ri 表示索引寄存器(%esp与ebp通常不作为索引寄存器, %esp常作栈顶寄存器);S表示比例因子, 为1, 2, 4, 8;
数据传输指令
数据传送指令(mov):
movl source, dest
将一个“双字”从source 移到 dest. 允许的操作数的类型包括:立即数、寄存器和存储器,如下图所示( 两个操作数不能同时是内存地址,因为x86中一条指令不能同时访问再次内存)
mov 常用的指令格式:
地址计算指令(lea):
leal src, dest
功能:用于计算表达式src的地址放到寄存器dest中。 其中,src表示地址的计算表达式, dest表示目的寄存器(注意:dest 不能为内存地址,只能是在寄存器)
补充: 由于lea 命令可以把一个合适的表达式只用一条指令表示出来,所以可以使用lea进行这一类型的整数计算,而不仅仅进行地址的计算。
双操作数指令:
addl src, dest dest = dest + src #求和 subl src, dest dest = dest - src #减法 imull src, dest dest = dest * src #乖法 sall src, dest dest = dest << src #逻辑左移 shll src, dest dest = dest << src #算术左移 sarl src, dest dest = dest >> src #逻辑右移 shrl src, dest dest = dest >> src #算术右移 xorl src, dest dest = dest ^ src #按位异或 andl src, dest dest = dest & src #按位与 orl src, dest dest = dest | src #按位或
单操作数指令:
x86-64下的通用寄存器
1. %eax, %edx, %ecx, %ebx, %esi, %edi, %dsp, %ebp 是 x86-32下的8个通用寄存器。 x
2. 86-64共有了16个通用寄存器。
3. %ebp/%rbp不再是专用寄存器。
4. %esp, 当前运行栈的栈顶地址,%ebp, 当前运行栈的“帧”地址。
5. 在x86-64下, 参数的通常使用寄存器进行传递。 具体为:当函数的参数少于7个时, 参数从左到右放入寄存器 rdi, rsi, rdx, rcx, r8, r9中。 当参数大于等于7个时, 后面的参数依次按从右向左顺序放入到栈中。
控制流
条件码
条件码由算术指令隐含设置
CF进位标志: 可以用于检测无符号整数运算的溢出;
ZF:零标志位,当运算结果为零时, ZF=1, 非零时为ZF=0;
SF符号位,当运算结果为负时,SF=1, 为正时SF= 0;
OF溢出标志, 当补码运算溢出时(即当两个正整数相加小于零或两个负数相加大于零时或正减负小于零或负减正大于零,就是溢出), OF=1。
比较指令
cmpl src2, src1 cmpq src2, src1
注意一个顺序,如果我们比较a 与 b 的大小,则应该写为 cmpl b, a; 这样才能写后面的setx 及jx相对应。
cmpl b, a 比较的目的是:根据 a-b的结果 去改变条件码的;
cmpl 指令常常与 setx 指令以及 jx 指令一起使用。
测试指令
testl src2, src1 testq src2, src1
测试的目的是: 根据 src1 & src2 的结果去修改条件码。
setx指令
读取当前的条件码(或者某些条件码的组合),并存入目的“字节”寄存器。(这里强调字节,意思就是结果为8个bit)
也可以说,如果满足描述的条件,即setx就会置1, 否则置0。
说明:1. 上图中的 description 是什么意思呢??它表示的意思就是 condition 中公式表达出来的意思,可以根据 condition里的公式推出来description的。
当我们比较 x 与 y 的大小时,用伪指令 cmpl y, x 表示以后, 我们就可以使用指令setx 来查看比较的结果,如 指令 setg 返回 x>y 的结果.
例子: x86-32下的C语言:
x86-64下的C语言:
jx 指令
依赖当前的条件码,选择下一条需要执行的语句。 如果满足条件,就跳转,否则就顺序执行。
例子:
备注: 跳转指令的效率不高(因为流水线设计),经常使用条件传送指令 cmovX 取代。 cmov 是从 i686开始的,不兼容i386. 当使用gcc 编译时,可以通过-march=i686选择编译带cmovX的汇编代码。比如下面的例子:
g