目录
一、寄存器的种类
80386有如下通用寄存器:
- AX,BX,CX,DX;SI,DI;SP,BP(16位)
- AH,AL;BH,BL;CH,CL;DH,DL(8位)
- 以E开头的寄存器为32位
- EAX(累加器,是算术运算的主要寄存器)
- EBX(基址寄存器,在内存中寻址时存放基址)
- ECX(计数器)
- EDX(数据寄存器)
- ESI(源变址)
- EDI(目标变址)
- ESP(堆栈指针)
- EBP(基址指针)
- EIP(程序计数器,存储的是将要执行的下一条指令放在内存中的地址)
- EFLAGS(保存的是根据运算得到的结果设置的条件码ZF,CF,SF,OF)
- 段寄存器:
- CS:代码段寄存器
- SS:堆栈段寄存器
- DS:数据段寄存器
- ES、FS、GS:附加数据段寄存器
-
(该图摘自教案,侵删)
- 内存有大小端之分,寄存器没有
- 整型占四个字节,短整型占两个字节,字符型占一个字节
- 指向字符/整数/字符串/数组的指针均占用四个字节(其实指针存储的就是数据在内存中的地址)
二、mov指令
mov后的字母表示操作数的长度
使用mov指令可以在寄存器与寄存器之间、内存与寄存器之间传送数据
- movl:用于传送32位的长字值(四个字节)/ 传送双字
- movw:用于传送16位的字值(两个字节)/ 传送字
- movb:用于传送8位的字值(一个字节)/ 传送字节
mov指令可以根据目的操作数的大小来决定传送的字节数
举例:movl $4, %eax 执行的是将立即数4送入到寄存器eax中;
movl %ebx, %eax 执行的是将寄存器ebx中的值送入寄存器eax中;
movl $values,%eax 表示将values的首地址送入寄存器eax中;
movl values,%eax 表示将以values为首地址共四个字节单元表示的值送入到寄存器eax中;
- movsbl %dh, %eax 表示将%dh进行符号扩展之后存入寄存器eax中(扩展位根据最高位来填充)
- movzbl %dh, %eax 表示将%dh进行零扩展之后存入寄存器eax中(扩展位由零来填充)
与查看数据相关的指令
--print &value,查看value变量在内存中的地址
--x/4bt 查看内存内容(b表示单字节,h表示双字节,w表示四字 节,g表示八字节)
x 按十六进制格式显示变量,a 按十六进制格式显示变量。
u 按十六进制格式显示无符号整型;o 按八进制格式显示变量。
t 按二进制格式显示变量;d 按十进制格式显示变量
c 按字符格式显示变量;f 按浮点数格式显示变量
s 按照字符串格式显示变量
汇编代码的格式: “ 指令 源操作数 ,目的操作数 ”
示例:movl $8 , %eax
操作数:立即数(immediate)、寄存器(register)、存储器(memory)
注意:CPU计算总是仅从寄存器直接存/取数据
三、lea指令
lea 指令是mov 指令的变形
lea 指令形式是从存储器读数据到寄存器,但实际并没有引用存储器,而是将有效地址写入目的操作数(必须是寄存器)
leal S,D 表示D <---&S,将S的地址给D
据说,Intel对于设计出LEA指令颇为得意,LEA指令可以在一个时钟周期内完成,执行效率高;
lea不在ALU里执行,而是在AGU(address generation unit)里执行,可提高并行效率;
如:加法需要1~3个时钟周期,乘法一般需要>10个时间周,除法需要几十甚至上百个时间周期
可以模拟一些三元运算
例如:%ebx =% eax +% edx,不使用lea需要多条指令,而使用lea: lea (%eax,%edx,1), %ebx
lea指令和mov指令的辨析
- leaq 8(%edi), %eax==> R[%eax] = 8 + R[%edi]
- movq 8(%edi), %eax==> R[%eax] = M[8 + R[%edi]]
movl $立即数a,size(%register)是指将立即数a存放到在内存中最终地址为寄存器%register中的值 + size中的单元去
注:括号内的是寄存器,括号外size是偏移值,可为负数。
四、跳转指令
- jmp L:无条件跳转指令,跳转至L
- jge L:使用cmpl R1,R2作为判断(有符号数减法,R2-R1),大于等于则跳转至L
- jae L:使用cmpl R1,R2作为判断(无符号数减法,R2-R1),大于等于则跳转至L
五、压栈/弹栈指令
- push S:压栈指令。对于pushl S,表示R[%esp] <-- R[%esp] – 4;M[R[%esp]] <-- S,其中,4为l的字节数,且假设栈往低地址扩展。
注意:push $value表示将value的首地址压入栈中
- pop D:出栈指令。对于popl D,表示D <-- M[R[%esp]];R[%esp] <-- R[%esp] + 4,同理
push与pop也可以自动根据操作数的长度来分配入栈/出栈时栈顶指针需要减小/增大的地址长度
六、sub/add指令
- subl %eax,%edx 表示将%edx-%eax的结果存放进寄存器edx中
- addl %eax,%edx 表示将%edx+%eax的结果存放进寄存器edx中
1008.s
在调试过程中输入“p value1”即可查看value1的值,输入“p &value1”即可查看地址(p是print的缩写)
此时用p来查看value2的值默认的是打印四个字节(即会将后两个字节中的数据打印出来),所以value2会是0x??030002(根据value2的值可以算得为0x00030002)
(上机验证正确)
分析“movl %ecx, value2”这条指令,此时value2代表的是value2所处单元的首地址,该条指令代表的就是从寄存器ecx中取出的四个字节的数据复制到以value2所处单元的首地址为首的四个内存单元
输入“p $ eax”可以查看寄存器eax中的值
1009.s
- ".section .data"这一段表示申明变量,value1为一个值为400的整型(int占用四个字节),values为一个存储了400,15,20,25,30,35,40,45,50,55,60的整型数组(每个整型数值占四个字节)
- ".section .text" ".global _start" "_start:"相当于固定格式(类似于C++中的头文件,命名空间等)
- 10-12行格式一样,以第十行为例,该指令将立即数1送入寄存器edi中
- 第13行,这个时候value1可以看做它所处内存单元的首地址,将从value1首地址开始的四个字节的值送入寄存器ecx中
- 14-16行同上
- 以第18行为例,该行是将以为values+%edi+%ebx*1为首地址之后的四个字节中的数据送入寄存器eax中
先根据“p &values”得到values的首地址,再输入“x /44bx values首地址”查看以values为首地址共11*4个字节的单元的数据,
执行第十四行(按照十六进制显示eax中的数据)得到
执行第十五行,得到
执行第十六行,得到