深入理解计算机系统(3.3)------操作数指示符和数据传送指令

  在上一篇博客 程序编码以及数据格式 中我们给出了一个简单的C程序,然后编译成了汇编代码。大家看不懂没关系,后面的博客我们将逐渐揭开一些汇编指令的神秘面纱。本篇博客我们将对操作数指示符和数据传送指令进行详细的介绍。

1、整数寄存器

  上一篇博客我们讲了在汇编语言中,如下的几个处理器状态是可见的:

  一、程序计数器(在 IA32 中通常称为 PC,用 %eip 表示):指示将要执行的下一条指令在存储器中的地址。

  二、整数寄存器文件:包含8个命名的位置,可以存储一些地址或者整数的数据。有的用来记录某些重要的程序状态,有的则用来保存临时数据。

  三、条件码寄存器:保存最近执行的算数或逻辑指令的状态信息,它们用来实现控制或数据流中的条件变化,比如用来实现 if 和 while 语句。

  四、浮点寄存器:存储浮点数。

  这里我们要讲的就是第三个整数寄存器,在 32 位 CPU 中包含一组 8 个存储 32 位值的寄存器。这些寄存器用来存储整数数据或指针。下图是 IA32 的整数寄存器:

  

  上述八个寄存器主要功能如下:

%eax,可存放一般数据,而且可作为累加器使用;
%ebx,可存放一般数据,而且可用来存放数据的指针(偏移地址);
%ecx,可存放一般数据,而且可用来做计数器,常常将循环次数用它来存放;
%edx,可存放一般数据,而且可用来存放乘法运算产生的部分积,或用来存放输入输出的端口地址(指针);
%esi,可存放一般数据,还可用于串操作中,存放源地址,对一串数据访问;
%edi,可存放一般数据,还可用于串操作中,存放目的地址,对一串数据访问;
%esp,用于寻址一个称为堆栈的存储区,通过它来访问堆栈数据;
%ebp,可存放一般数据,用来存放访问堆栈段的一个数据区,作为基地址;

  在大多数情况下,%eax、%ecx、%edx、%ebx、%esi、%edi等6个寄存器可以看做通用寄存器,对它们的使用没有限制;%esp、%ebp两个寄存器保存着指向程序栈中重要位置的指针,只有根据栈管理的标准惯例才能修改这两个寄存器中的值。

  这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个寄存器进行详细讲解。

2、操作数指示符

  我们知道大多数指令都有一个或多个操作数(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),表示的是最常用的形式,分为四个部分,

  一、Imm 是立即偏移数

  二、Eb 是基址寄存器

  三、Ei 是变址寄存器

  四、s 是比例因子,必须是 1、2、4或8

  然后有效地址计算公式为: Imm + R[Eb]+R[Ei]*s。比如对于2(%esp,%eax,4)这个操作数来讲,它代表的是内存地址为2+%esp+4*%eax的存储器区域的值。

3、数据传送指令

  数据传送指令:将数据从一个位置复制到另一个位置的指令。简单来说就是复制指令。

  将源操作数的值复制到目的操作数中并覆盖。源操作数指定的值是一个立即数,存储在寄存器或存储器中。目的操作数指定一个位置,要么是一个寄存器,要么是一个存储器地址。在 IA32 中还有一条限制,传送指令的两个操作数不能都执行存储器位置。

  将一个值从一个存储器位置复制到另一个存储器位置需要两条指令:(就和宋丹丹把大象送进冰箱的步骤一样)

  ①、第一条指令将源值加载到寄存器中

  ②、第二条指令将该寄存器值写入到目的位置。

  下图是许多不同的指令类:

  

4、MOV 指令

  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去复制一个字节或者两个字节。

5、MOVS指令  

  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位表示。

6、MOVZ 指令

  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。

7、push 和 pop 指令

  我们知道 栈 是一个数据结构,可以添加或删除值,遵循“后进先出”的原则。

  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 指向的地址总是栈顶,任何存储在栈顶之外的数据都认为是无效的。

8、总结

  本章主要介绍了操作数指示符,需要我们理解几种表达式的计算方法。接着介绍了几种数据传送指令,包括MOV,MOVS,MOVZ,PUSH和POP等,总体上来看不难理解,我们在了解这些指令后,再回头看那些汇编代码,应该会理解很多了。下一篇博客我们将进一步介绍汇编指令——算术和逻辑操作。

  

时间: 2024-09-29 16:33:52

深入理解计算机系统(3.3)------操作数指示符和数据传送指令的相关文章

《深入理解计算机系统(原书第三版)》pdf

下载地址:网盘下载 内容简介  · · · · · · 和第2版相比,本版内容上*大的变化是,从以IA32和x86-64为基础转变为完全以x86-64为基础.主要更新如下: 基于x86-64,大量地重写代码,首次介绍对处理浮点数据的程序的机器级支持. 处理器体系结构修改为支持64位字和操作的设计. 引入更多的功能单元和更复杂的控制逻辑,使基于程序数据流表示的程序性能模型预测更加可靠. 扩充关于用GOT和PLT创建与位置无关代码的讨论,描述了更加强大的链接技术(比如库打桩). 增加了对信号处理程序

《深入理解计算机系统(原书第2版)》pdf

下载地址:网盘下载 内容简介  · · · · · · 本书从程序员的视角详细阐述计算机系统的本质概念,并展示这些概念如何实实在在地影响应用程序的正确性.性能和实用性.全书共12章,主要内容包括信息的表示和处理.程序的机器级表示.处理器体系结构.优化程序性能.存储器层次结构.链接.异常控制流.虚拟存储器.系统级I/O.网络编程.并发编程等.书中提供大量的例子和练习,并给出部分答案,有助于读者加深对正文所述概念和知识的理解. 本书的最大优点是为程序员描述计算机系统的实现细节,帮助其在大脑中构造一个

深入理解计算机系统之程序的机器级表示部分学习笔记

不论我们是在用C语言还是用JAVA或是其他的语言编程时,我们会被屏蔽了程序的机器级的实现.机器语言不需要被编译,可以直接被CPU执行,其执行速度十分  快.但是机器语言的读写性与移植性较高级语言低.高级语言被编译后便成为了汇编语言,汇编语言十分接近机器语言.之后汇编代码会转化为机器语言.虽然现代  的编译器能帮助我们将高级语言转化为汇编语言,解决了不少问题,但是对于一个严谨的程序员来说,需要做到能够阅读和理解汇编语言.我们主要围绕Intel来讲  解. 一  Intel处理器的历史演变 Inte

速读《深入理解计算机系统》

本周速读了<深入理解计算机系统>这本书,这本书从程序员的角度介绍了计算机系统的内在运作,展示了一些本质概念是如何实际的影响应用程序的正确性.性能和实用性的.收获如下: 1.关于进程和线程 进程是操作系统对运行程序的一种抽象,是指计算机中已运行的程序,是系统进行资源分配和调度的基本单位,是操作系统结构的基础. 线程有时被称为轻量级进程,是操作系统能够进行运算调度的最小单位.线程是进程中的一个实体,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥

《深入理解计算机系统》第五周学习笔记

第三章 程序的机器级表示 一.知识点总结 (一) 1.计算机执行机器代码,用字节序列编码低级的操作,,包括处理数据.管理存储器.读写存储设备上的数据.利用网络通信. 2.GCC C语言编译器以汇编代码的形式产生输出,汇编代码是机器代码的文本表示.给出程序中每条指令,然后GCC调用汇编器和链接器,从而根据汇编代码生成可执行的机器代码 3.逆向过程——通过研究系统和逆向工作来了解系统的创建过程 (二)程序编码 1.gcc -01 -o p p1.c p2.c  (-01告诉编译器使用第一级优化) 2

深入理解计算机系统(3.3)---数据传送(或者说复制)指令详解

本文转载地址:http://www.cnblogs.com/zuoxiaolong/p/computer15.html 引言 上一章我们已经介绍了汇编语言的基础部分,包括数据格式.寄存器以及操作数的标识方式,接下来我们就应该去认识一下汇编语言当中的各个指令了.这些指令大多数都非常简单,但是组合在一起却能模拟出我们程序当中想要的任何效果,确实是十分神奇的一件事. 数据传送指令 数据传送指令的目的是为了将一个数据从一个位置复制到另外一个位置.既然如此,那么数据传送指令就会包含一个源操作数和一个目的操

C语言杂记 -- 简陋的&lt;深入理解计算机系统&gt;笔记

程序的表示 l 32位64位操作系统是由CPU寄存器的位数决定,即虚拟寻址的范围为2^32.2^64. l 字节的大端小端法是以字节为基本单位的:比如十进制的7在十六位机器上表示 · 地址 100 101 大端法 00000000 00000111 小端法 00000111 00000000 l 大部分编译器默认进行算数右移和用补码表示复数 l 对于移位k来说,实际移位量为k mod 2^w(w为所移动数据类型的位数) l 对于整型常量来说c编译器都是先将其看成正数,如果前面有 - 运算符则对其

《深度理解计算机系统》读书笔记3-汇编

使用的机器为Intel IA32位. 1.机器指令的设计方式是,从某个位置开始,可以将字节唯一地解释成机器指令: 2.数据传送指令的两个操作数不能都是存储器. 3.数据传送指令分为三类: (1)movb,movw,movl:两个操作数的长度要一样 (2)movsbw,movsbl,movswl:源操作数段,目的操作数长,采用符号扩展(3)movzbw,movzbl,movzwl:源操作数段,目的操作数长,采用零扩展 (4)pushl,popl:将操作数压栈或者弹出栈顶元素到操作数.栈中一个位置的

深入理解计算机系统(3.2)---数据格式、访问信息以及操作数指示符

本文转载地址:http://www.cnblogs.com/zuoxiaolong/p/computer14.html 引言 本文的内容其实可以成为汇编语言的基础,因为汇编语言大部分时候是在操作一些我们平时开发看不到的东西,因此本文的目的就是搞清楚,汇编语言都是在操作些什么东西.或者更准确的说,各种汇编指令都是在操作什么样的对象. 汇编层次的对象 在平时的开发过程中,CPU处理器的状态对开发者是隐藏的,我们看不到CPU当中各个对象的状态.但是在汇编语言中,我们可以清楚的看到这些对象的状态,其中C