数据传送指令详解

数据传送指令详解

前言

上一章我们说了汇编语言的基础,包括数据格式,寄存器以及操作数的标识方式,接下来我们就应该去认识一下hiU币按语言当红真难过的格各个指令了.这些指令大部署很简单,但是组合在一起却能模拟出我们程序当中香烟的任何效果,确实很神奇.

数据传送指令

数据传送指令的目的是我了将一个数据从一个位置复制到另一个位置.既然如此,那么数据传送至零就会包含一个源操作数和一个目的操作数,指令会将源操作数的值复制到目的操作数并覆盖.

数据传送指令一共可以分为5种,分别是mov,movs,movz,push以及pop.如果你多少懂一点编程语言的话,看名字就能知道一个大概,不过在这里我还是说一下各个指令的作用.

mov指令

mov指令的作用是将源操作数S中的数据复制到目的操作数D中,mov指令有一个数据格式和两个操作数,因此一般的形式为[movx S D].其中x为数据格式,S为源操作数,D为目的操作数.

举个简单的案例,比如我们有一条指令为movl %edx %eax.那么他的执行过程如下图所示:

可以看到,在指令执行之后,%edx寄存器当中的内容就会被复制到%eax寄存器.需要一提的是,mov指令可以在后面加上任何数据格式,比如上面这一过程中,数据格式为4个字节,也就是双字.因此不难推断出,我们还可以使用movb和movw去复制一个字节或两个字节.

movs指令

movs指令的作用是将源操作数S中的数据做符号扩展后,再复制到目的操作数D中,movs指令有两个数据格式和两个操作数,因此一般的形式为[movsxy S D].其中x,y位数据格式,S为源操作数,D为目的操作数.其中x,y的组合一共有三种,分别是bw,bl,wl,这三个组合代表的意思分别是单字节到双字节,单字节到双字以及双字节到双字.

还是举个例子,对于指令

movswl %dx %eax

来说,它的作用如下图:

这里为了看出来符号位的扩展,因为我们在这里使用了十六进制的整数表示方式.可以看到,movs指令将0x8FFF扩展以后存入%eax寄存器,其中%dx为寄存器%edx的后16位表示.

movz指令

movz指令的作用是将源操作数S做零扩展后,再复制到目的操作数中.它与movs指令十分相似,也有两个数据格式和两个操作数,因此一般形式为[movzxy S D].各个字母代表的含义和movsxy代表的一样.

还是看案例,对于指令

movzwl %dx %eax

来说,他的作用和上面的movs有何不同.

可以看出,movs和movz指令十分相似,只是这里扩展后,目标寄存器%eax的前16位是0而不是1.

push指令

push指令与上面的mov族的指令不同,他的目的是操作数被固定为栈顶,因此他的指令当中没有目的操作数另外友谊县需要注意,他在进行复制操作之前,需要移动栈顶指针(-4).push指令的一般形式为[pushl S],其中l代表数据格式为双字,S为源操作数,目的操作数默认为栈顶

案例,比如

pushl %edx

这条指令,他的任务是将%edx寄存器的值复制到栈顶.我们首先来看一下命令执行前,寄存器以及存储器的状态.

可以看到,寄存器%ebp和%esp分别指向帧指针和栈指针,而%esp实际上就是指向栈顶.由于现在栈顶位于-16的位置,因此若要将%dex压入栈,则现需要将栈顶移动到-20的位置,然后再进行赋值,移动后的状态如下所示:

可以看到,这里的栈指针的位置已经发生了变化,向下移动了四位,并且将%edx寄存器的值放入新的栈顶,因此pushl %edx指令就相当于下面两条指令:

subl $4,%esp
movl %edx,(%esp)

这里可以看出,其实push指令做了一个隐藏操作,就是移动栈指针(-4),这一点希望能引起大家的注意.

坚持住,还有最后一个命令!

pop指令

pop指令与push指令的做的相反的操作,一个是入栈一个是出栈.对于pop指令来说,他的源操作数背负定位栈顶,相反,它会先进行复制操作,然后再移动栈指针.pop指令的一般形式为[popl D],其中l代表数据类型,D为目的操作数,源操作数默认为栈顶.

案例,考虑

popl %edx

这条指令的效果,他会将栈顶的值弹出到寄存器%edx.首先来看执行之前,寄存器以及存储器的状态.

接下来执行pop指令时,会相减栈顶的值复制到%edx,然后在将栈指针移动(+4).我们来看一下它执行后的状态.

可以看到,之前栈顶的内容已经 被弹出到%edx寄存器,并且当前栈顶已经移动到了-16的位置,也就是进行了+4操作.因此popl %edx指令就相当于下面这两条指令:

movl (%esp),%edx
addl $4,%esp

这里能够看出,其实popl指令也同样做了一个隐藏的操作,就是移动栈指针(+4).

说了很多,貌似有人会说都是花架子,下面咱们就结合具体的案例来看看

数据复制示例

上面我们说了几乎所有的复制指令,接下来的一小段代码,让我们来看一下这些数据指令,如何完成我们的程序操作.

simple(int *xp,int y){
    int t=*xp;
    *xp=y;
    return t;
}

上面是一个简单的C语言程序,它其中包含了一些复制操作,我们来看看他的汇编代码.使用GCC -O1 -S sum.c来获取我们的汇编代码,并使用cat sum.c来查看一下:

.file    "sum.c"
    .text
.globl simple
    .type    simple, @function
simple:
    pushl    %ebp
    movl    %esp, %ebp
    //以上为栈的建立部分
    movl    8(%ebp), %edx
    movl    (%edx), %eax
    movl    12(%ebp), %ecx
    movl    %ecx, (%edx)
    //以下为栈的完成部分
    popl    %ebp
    ret
    .size    simple, .-simple
    .ident    "GCC: (Ubuntu 4.4.3-4ubuntu5.1) 4.4.3"
    .section    .note.GNU-stack,"",@progbits

分析这段汇编代码的时候,我们应该分为三个部分来看待,首先是栈的建立,然后是使用,最后是完成部分.看到这里,里面几乎全是数据复制指令,我们先开看看栈的建立部分.

其实地狱与一开始pushl和movl指令来说,他们主要做了两件事.第一个是将原来的帧指针备份到栈顶,然后再将栈指针和帧指针统一指向这个新的栈顶,也就是完成了一个新站的建立.它在完成后,栈的状态如下所示.

可以看到,寄存器%ebp和寄存器%esp都指向当前栈指针的位置,其中变量xp位于+8的位置,而y位于+12的位置.由于xp是一个指针变量,因此它会指向一个内存中的区域,其中的值为*xp.

了解完寄存器和存储器的转台,此时栈已经建立完毕,接下来我们看紧接着的一句汇编代码的作用.

movl  8(%ebp),	%edx

这一句将内存地址为%ebp+8的值复制到%edx,很明显,从上面的图中可以看出,%ebp+8这个位置存储着xp变量.这一句指令做了一个简单的操作,就是将xo提取到%edx寄存器,如下所示:

此时已经将%edx的值改变为了变量xp,看接下来的一句操作.

movl (%edx) ,%eax

这一句话将内存地址为%edx的值赋给类寄存器%eax,并准备返回值.此时%edx寄存器的值已经改变为了xp变量,因此(%edx)其实就是*xp,而%eax寄存器一般回座位函数的返回值,因此他其实代替了临时变量t.执行后的状态如下所示.

此时其实已经完成了程序中的int t=*xp以及为return t准备好了返回值,接下来的一句汇编代码也很简单,如下.

movl 12(%ebp),%ecx

它的作用是将地址为%ebp+12的值复制到寄存器%ecx,从图中可以看出,%ebp+12就是存储的变量y.因此他的作用就是将y复制到寄存器%ecx,如下所示:

上面这一步很简单,我们来看最后一步操作,如下:

movl %ecx ,(%edx)

他的作用是将%ecx寄存器的值复制到内存中%edx的位置.此时%ecx的值为y,而%edx中为xp,因此目的操作数则为xp执行的位置,也就是*xp.这一句话执行的就是程序代码当中*xp=y这个操作,它执行后的状态如下所示:

可以看到,在执行了*xp=y以后,xp指针所指向的位置,其值已经变成了y.此时程序其中已经基本运行完毕,剩下的工作也就是栈的完成操作了,也就是popl指令.在栈完成之后,也就是pop指令执行之后,当前帧会恢复到调用者的帧上面去,如下所示:

此时当前帧已经恢复到了调用者的帧,最后ret指令会改变程序计数器(PC)的值,然后跳出子函数,继续执行调用者当中的代码.到此,我们的数据复制实力就完成了,尽管着了例子不难,但是很能够说明问题,只要能了解了这个过程,相信一些复杂的汇编指令也只是分析的时间长点罢了.

小小的结一下

一般人家都说,字不如表,表不如图,有了这么多的好图,你肯定想求个种子啥的对吧,这不是让你看图猜链接....

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-20 21:30:55

数据传送指令详解的相关文章

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

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

16位汇编第五讲各种指令详解第一讲

汇编指令详解 8080指令详解 1.8086系统下,Inter指令系统共有117条指令(看似很多,分一下类) 1.数据传送类指令(专门传送数据的) 2.算术运算类指令(加减乘除的运算的) 3.位操作类指令(或  异货 与 -.) 4.串操作类指令 (内存拷贝,内存连续地址拷贝的操作) 5.控制转移类指令(跳转,比如C语言的Goto) 6.处理机控制类指令(计算机的待机 ,重启 等等,让CPU待机睡眠的指令) 学习指令的注意事项 1.指令的功能,也就是这个指令可以实现什么操作.通常的话,指令就是指

迈向angularjs2系列(2):angular2组件和指令详解

<%= INIT %> 内容 一:angular2 helloworld! 为了简单快速的运行一个ng2的app,那么通过script引入预先编译好的angular2版本和页面的基本框架. index.html: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> &l

16位汇编第六讲汇编指令详解第二讲

16位汇编第六讲汇编指令详解第二讲 1.比较指令 CMP指令 1.CMP指令是将目的操作数减去源操作数,按照定义相应的设置状态标志 2.CMP指令执行的功能与SUB指令(相减指令)一样,但是不同的是CMP指令之根据结果设置标志位 而不修改值 可以操作的指令格式 CMP reg,imm/reg/mem CMP mem,imm/reg 上面是CMP指令的语法,具体的也可以查询帮助文档,inter手册 inter手册查的办法 第一个框代表了CMP指令的所有语法 比如 reg,reg 表示可以比较寄存器

iret指令详解

在"实地址模式"中,IRET 指令执行到中断程序或过程的远返回.在执行此操作的过程中,处理器从堆栈将返回指令指针.返回代码段选择器以及 EFLAGS 映像分别弹入 EIP.CS 以及 EFLAGS 寄存器,然后恢复执行中断的程序或过程. 在"保护模式"中,IRET 指令的操作取决于以下因素:EFLAGS 寄存器中 NT(嵌套任务)与 VM 标志的设置,以及当前堆栈中存储的 EFLAGS 映像中的 VM 标志的设置.根据这些标志的设置,处理器执行以下类型的中断返回:

Angularjs 事件指令 input 相关指令 和样式指令 DOM 操作指令详解

Angularjs 事件指令 input 相关指令 和样式指令DOM 操作指令详解学习要点:1. AngularJs 事件指令2. input 相关指令3. 样式指令4. DOM 操作指令5. ngBind/ngBindHtml/ngBindTemplate 重点6. ng-init ng-mode ng-model-options ng-controler 1. Angularjs 事件指令自己研究:ng-click/dbclickng-mousedown/upng-mouseenter/le

#pragma 预处理指令详解

原文链接:http://blog.csdn.net/jx_kingwei/article/details/367312 #pragma  预处理指令详解              在所有的预处理指令中,#pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作.#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征.依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的.     

pragma comment的使用 pragma预处理指令详解

pragma comment的使用 pragma预处理指令详解 #pragma comment( comment-type [,"commentstring"] ) 该宏放置一个注释到对象文件或者可执行文件.comment-type是一个预定义的标识符,指定注释的类型,应该是compiler,exestr,lib,linker之一.commentstring是一个提供为comment-type提供附加信息的字符串,Remarks:1.compiler:放置编译器的版本或者名字到一个对象

几种基本汇编指令详解

几种基本汇编指令详解 常见寄存器 寄存器 16位 32位 64位 累加寄存器 AX EAX RAX 基址寄存器 BX EBX RBX 计数寄存器 CX ECX RCX 数据寄存器 DX EDX RDX 堆栈基指针 BP EBP RBP 变址寄存器 SI ESI RSI 堆栈顶指针 SP ESP RSP 指令寄存器 IP EIP RIP 汇编指令 mov movb(8位).movw(16位).movl(32位).movq(64位) 寄存器寻址: movl %eax, %edx eax -> edx