在上一篇博客 程序编码以及数据格式 中咱们给出了一个简单的C程序,而后编译成了汇编代码。你们看不懂不要紧,后面的博客咱们将逐渐揭开一些汇编指令的神秘面纱。本篇博客咱们将对操做数指示符和数据传送指令进行详细的介绍。html
上一篇博客咱们讲了在汇编语言中,以下的几个处理器状态是可见的:数组
1、程序计数器(在 IA32 中一般称为 PC,用 %eip 表示):指示将要执行的下一条指令在存储器中的地址。数据结构
2、整数寄存器文件:包含8个命名的位置,能够存储一些地址或者整数的数据。有的用来记录某些重要的程序状态,有的则用来保存临时数据。post
3、条件码寄存器:保存最近执行的算数或逻辑指令的状态信息,它们用来实现控制或数据流中的条件变化,好比用来实现 if 和 while 语句。编码
4、浮点寄存器:存储浮点数。指针
这里咱们要讲的就是第三个整数寄存器,在 32 位 CPU 中包含一组 8 个存储 32 位值的寄存器。这些寄存器用来存储整数数据或指针。下图是 IA32 的整数寄存器:htm
上述八个寄存器主要功能以下:blog
%eax,可存放通常数据,并且可做为累加器使用; %ebx,可存放通常数据,并且可用来存放数据的指针(偏移地址); %ecx,可存放通常数据,并且可用来作计数器,经常将循环次数用它来存放; %edx,可存放通常数据,并且可用来存放乘法运算产生的部分积,或用来存放输入输出的端口地址(指针); %esi,可存放通常数据,还可用于串操做中,存放源地址,对一串数据访问; %edi,可存放通常数据,还可用于串操做中,存放目的地址,对一串数据访问; %esp,用于寻址一个称为堆栈的存储区,经过它来访问堆栈数据; %ebp,可存放通常数据,用来存放访问堆栈段的一个数据区,做为基地址;
在大多数状况下,%eax、%ecx、%edx、%ebx、%esi、%edi等6个寄存器能够看作通用寄存器,对它们的使用没有限制;%esp、%ebp两个寄存器保存着指向程序栈中重要位置的指针,只有根据栈管理的标准惯例才能修改这两个寄存器中的值。ip
这8个寄存器均可以做为16位(字)或32位(双字)来访问。字节操做指令能够独立的读或者写%eax、%ecx、%edx、%ebx等4个寄存器的2个低位字节,由于%ax、%cx、%dx、%bx这4个16位寄存器又可分别分红ah,al ;bh,bl;ch,cl;dh,dl的8位寄存器。内存
这里你们也只须要有个眼熟就行了,后面咱们将对这个8个寄存器进行详细讲解。
咱们知道大多数指令都有一个或多个操做数(operand),指示出执行一个操做中要引用的源数据值,以及放置结果的目标位置。下图是 IA32 支持的多种操做数格式:
上图咱们能够看出源数据值能够是常数形式给出,或者是从寄存器或存储器中读出。而结果能够存放在寄存器或存储器中。咱们将不一样的操做数分为以下三种类型:
①、当即数(immediate):书写方式是$符号后跟一个标准C表示的整数,好比$52,$0x1F等等。任何能放进一个32位的字里面的数值均可以作当即数。
②、寄存器(register):它表示某个寄存器的内容,能够是8个32位寄存器中的一个(好比%eax),也能够是8个16位寄存器中的一个(好比%ax),还能够是8个单字节寄存器寄存器(好比%al)。上图是用Ea来表示任意寄存器a,用引用 R[Ea]来表示它的值。
③、存储器(memory):它会根据计算出来的地址(一般称为有效地址)来访问某个存储器位置。咱们将存储器当作一个很大的字节数组,用符号Mb[Addr] 表示对存储在存储器中从地址 Addr 开始的 b 个字节值的引用。上图省略了下方的 b.
从上图咱们知道,第一行是当即数,第二行则是寄存器,剩下的所有是存储器。其中最后一行存储器语法 Imm(Eb,Ei,s),表示的是最经常使用的形式,分为四个部分,
1、Imm 是当即偏移数
2、Eb 是基址寄存器
3、Ei 是变址寄存器
4、s 是比例因子,必须是 一、二、4或8
而后有效地址计算公式为: Imm + R[Eb]+R[Ei]*s。好比对于2(%esp,%eax,4)这个操做数来说,它表明的是内存地址为2+%esp+4*%eax的存储器区域的值。
数据传送指令:将数据从一个位置复制到另外一个位置的指令。简单来讲就是复制指令。
将源操做数的值复制到目的操做数中并覆盖。源操做数指定的值是一个当即数,存储在寄存器或存储器中。目的操做数指定一个位置,要么是一个寄存器,要么是一个存储器地址。在 IA32 中还有一条限制,传送指令的两个操做数不能都执行存储器位置。
将一个值从一个存储器位置复制到另外一个存储器位置须要两条指令:(就和宋丹丹把大象送进冰箱的步骤同样)
①、第一条指令将源值加载到寄存器中
②、第二条指令将该寄存器值写入到目的位置。
下图是许多不一样的指令类:
MOV 类由三条指令组成:movb,movw和 movl。指令格式为 [movx S D],表示将源操做数S中的数据复制到目的操做数D中。三种指令的区别是它们分别是在大小为 1,2和4个字节的数据上进行操做。
这里举一个简单的例子,好比咱们有一条指令为movl %edx %eax。那么它的执行过程就以下图所示。
上图引用至:http://www.cnblogs.com/zuoxiaolong/p/computer15.html
在指令执行以后,%edx寄存器当中的内容会被复制到%eax寄存器。数据格式则为四个字节,也就是双字。咱们还可使用movb和movw去复制一个字节或者两个字节。
MOVS指令格式为 [movsxy S D],其中x、y为数据格式,S为源操做数,D为目的操做数。x、y的组合有三种,分别是bw,bl,wl,分别表示字节(8位)传送到字(16位),字节(16位)传送到双字(32位),字(16位)传送到双字(32位)。
将较小的源数据复制到一个较大的数据位置。高位用符号位扩展,即目的位置的全部高位用源值的最高位数值进行填充。
好比对于指令movswl %dx %eax来说,它的做用以下图所示。
上图引用至:http://www.cnblogs.com/zuoxiaolong/p/computer15.html
这里使用了十六进制的整数表示方式。能够看到,movs指令将0x8FFF扩展之后存入%eax寄存器,其中%dx为寄存器%edx的后16位表示。
MOVZ 指令和上面的MOVS 指令十分类似。指令格式为 [movzxy S D],其中x、y为数据格式,S为源操做数,D为目的操做数。x、y的组合有三种,分别是bw,bl,wl,分别表示字节(8位)传送到字(16位),字节(16位)传送到双字(32位),字(16位)传送到双字(32位)。
将较小的源数据复制到一个较大的数据位置。高位用0扩展,即目的位置的全部高位用0进行填充。
好比对于指令movzwl %dx %eax来说,它的做用以下图所示。
上图引用至:http://www.cnblogs.com/zuoxiaolong/p/computer15.html
扩展后,目标寄存器%eax的前16位为0而再也不是1。
咱们知道 栈 是一个数据结构,能够添加或删除值,遵循“后进先出”的原则。
push:把数据压入栈中,添加数据。
pop:把数据移出栈,删除数据。注意移出的值老是最近被压入而仍然在栈中的值。
栈能够实现为一个数组,老是从数组的一端插入或删除元素。而这一端称为栈顶,在 IA32 中,程序栈存放在存储器某个区域,以下图所示:
注意因为操做数字节的不一样,pushl 是将双字(32位)压入栈中;popl 是移出双字。
将一个双字值压入栈中,首先要将栈指针减4,而后将值写到新的栈顶地址。所以指令 pushl %ebp 等价于下面两条指令:
subl $4,%esp
movl %ebp,(%esp)
上图所示,当 %esp 为0x108,%eax为0x123时,执行指令 pushl %ebp 的效果。首先 %esp 会减4,获得0x104,而后会将 0x123 存放到存储器地址 0x104处。
将一个双字值从栈顶移出,首先要从栈顶位置读出数据,而后将栈指针加4。所以指令 popl %eax 等价于下面两条指令:
movl (%esp),%eax
addl $4,%esp
上图所示,先从存储器中读取值 0x123,再写到寄存器 %edx中,而后寄存器%esp的值将增长回到0x108。
注意值0x123仍然会保存在存储器0x104中,直到被覆盖(好比被另外一条入栈操做覆盖)。不管如何,%esp 指向的地址老是栈顶,任何存储在栈顶以外的数据都认为是无效的。
本章主要介绍了操做数指示符,须要咱们理解几种表达式的计算方法。接着介绍了几种数据传送指令,包括MOV,MOVS,MOVZ,PUSH和POP等,整体上来看不难理解,咱们在了解这些指令后,再回头看那些汇编代码,应该会理解不少了。下一篇博客咱们将进一步介绍汇编指令——算术和逻辑操做。